bugfixes and improvements

This commit is contained in:
Scott Richmond 2024-06-15 21:51:56 -04:00
parent d64467ef6d
commit 07c6d23587

View File

@ -9,26 +9,26 @@
& some forward declarations
& TODO: fix this so that we don't need (as many of) them
fn first
fn append
fn some?
fn update!
fn string
fn join
fn neg?
fn atan/2
fn mod
fn assoc & ?
fn dict
fn get
fn unbox
fn store!
fn turn/rad
fn deg/rad
fn floor
fn and
fn append
fn apply_command
fn assoc & ?
fn atan/2
fn deg/rad
fn dict
fn first
fn floor
fn get
fn join
fn mod
fn neg?
fn some?
fn state/call
fn store!
fn string
fn turn/rad
fn unbox
fn update!
& the very base: know something's type
fn type {
@ -36,6 +36,30 @@ fn type {
(x) -> base :type (x)
}
& 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
(_) -> 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
}
& ...and if two things are the same
fn eq? {
"Returns true if all arguments have the same value."
@ -166,25 +190,10 @@ fn list? {
}
fn list {
"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. Associative collections return lists of (key, value) tuples."
"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)
}
& TODO: make this work with Janet base
fn set {
"Takes an ordered collection--list or tuple--and turns it into a set."
(xs as :list) -> base :into (${}, xs)
(xs as :tuple) -> base :into (${}, xs)
}
fn set? {
"Returns true if a value is a set."
(xs as :set) -> true
(_) -> false
}
& add a contains? or has? function
fn fold {
"Folds a list."
(f as :fn, xs as :list) -> fold (f, xs, f ())
@ -245,30 +254,29 @@ fn concat {
(xs, ys, ...zs) -> fold (concat, zs, concat (xs, ys))
}
& the console: sending messages to the outside world
& the console is *both* something we send to the host language's console
& ...and also a list of messages.
& box console = []
& fn flush! {
& "Clears the console, and returns the messages."
& () -> {
& let msgs = unbox (console)
& store! (console, [])
& msgs
& }
& }
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 add_msg! {
& "Adds a message to the console."
& (msg as :string) -> update! (console, append (_, msg))
& (msgs as :list) -> {
& base :print! (("adding msg", msgs))
& let msg = do msgs > map (string, _) > join
& base :print! (("msg: ", msg))
& update! (console, append (_, msg))
& }
& }
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)
}
fn print! {
"Sends a text representation of Ludus values to the console."
@ -283,15 +291,6 @@ fn show {
(x) -> base :show (x)
}
fn prn! {
"Prints the underlying Clojure data structure of a Ludus value."
(x) -> {
base :prn (x)
& add_msg! (x)
:ok
}
}
fn report! {
"Prints a value, then returns it."
(x) -> {
@ -299,7 +298,7 @@ fn report! {
x
}
(msg as :string, x) -> {
print! (concat (msg, show (x)))
print! (concat ("{msg} ", show (x)))
x
}
}
@ -332,7 +331,7 @@ fn join {
([]) -> ""
([str as :string]) -> str
(strs as :list) -> join (strs, "")
([str], separator as :string) -> str
([str as :string], separator as :string) -> str
([str, ...strs], separator as :string) -> fold (
fn (joined, to_join) -> concat (joined, separator, to_join)
strs
@ -398,13 +397,7 @@ fn sentence {
(strs as :list) -> join (strs, " ")
}
& in another prelude, with a better actual base language than Java (thanks, Rich), counting strings would be reasonable but complex: count/bytes, count/points, count/glyphs. Java's UTF16 strings make this unweildy.
& TODO: add trim, trim/left, trim/right; pad/left, pad/right
& ...also a version of at,
&&& references: mutable state and state changes
&&& boxes: mutable state and state changes
fn box? {
"Returns true if a value is a box."
@ -650,17 +643,17 @@ fn at {
fn first {
"Returns the first element of a list or tuple."
(xs) -> at (xs, 0)
(xs) if ordered? (xs) -> at (xs, 0)
}
fn second {
"Returns the second element of a list or tuple."
(xs) -> at (xs, 1)
(xs) if ordered? (xs) -> at (xs, 1)
}
fn last {
"Returns the last element of a list or tuple."
(xs) -> at (xs, dec (count (xs)))
(xs) if ordered? (xs) -> at (xs, dec (count (xs)))
}
fn butlast {
@ -731,9 +724,6 @@ fn or {
(x, y, ...zs) -> fold (or, zs, base :or (x, y))
}
&&& associative collections: dicts, structs, namespaces
& TODO?: get_in, update_in, merge
& TODO?: consider renaming these: put & take, not assoc/dissoc
fn assoc {
"Takes a dict, key, and value, and returns a new dict with the key set to value."
() -> #{}
@ -791,30 +781,6 @@ fn diff {
}
}
fn coll? {
"Returns true if a value is a collection: dict, list, pkg, tuple, or set."
(coll as :dict) -> true
(coll as :list) -> true
(coll as :tuple) -> true
(coll as :set) -> true
(coll as :pkg) -> true
(_) -> false
}
fn ordered? {
"Returns true if a value is an indexed collection: list or tuple."
(coll as :list) -> true
(coll as :tuple) -> true
(_) -> 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
}
& TODO: consider merging `get` and `at`
fn get {
"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."
@ -843,15 +809,11 @@ fn dict? {
(_) -> false
}
& TODO: make this less awkward once we have tail recursion
fn each! {
"Takes a list and applies a function, presumably with side effects, to each element in the list. Returns nil."
(f! as :fn, []) -> nil
(f! as :fn, [x]) -> { f! (x); nil }
(f! as :fn, [...xs]) -> loop (xs) with {
([x]) -> { f! (x); nil }
([x, ...xs]) -> { f! (x); recur (xs) }
}
(f! as :fn, [x, ...xs]) -> { f! (x); each! (f!, xs) }
}
&&& Trigonometry functions
@ -938,18 +900,38 @@ fn atan/2 {
}
fn mod {
"Returns the modulus of num and div. Truncates towards negative infinity."
"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
(num as :number, div as :number) -> base :mod (num, div)
}
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 {
"Returns the square root of a number."
(x as :number) -> base :sqrt (x)
"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 {
@ -957,18 +939,21 @@ fn sum_of_squares {
() -> 0
(x as :number) -> square (x)
(x as :number, y as :number) -> add (square (x), square (y))
(x, y, ...zs) -> fold (sum_of_squares, zs, sum_of_squares (x, y))
(x, y, ...zs) -> fold (
fn (sum, z) -> add (sum, square (z))
zs
sum_of_squares (x, y))
}
fn dist {
"Returns the distance from the origin to a point described by (x, y)."
"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)
}
&&& more number functions
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. Alternately, given a list, it returns a random member of that list."
"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 ()
(n as :number) -> mult (n, random ())
(m as :number, n as :number) -> add (m, random (sub (n, m)))
@ -980,6 +965,7 @@ fn random {
let key = do d > keys > random
get (key, d)
}
(s as :set) -> do s > list > random
}
fn random_int {
@ -1048,10 +1034,12 @@ fn unwrap_or {
fn assert! {
"Asserts a condition: returns the value if the value is truthy, panics if the value is falsy. Takes an optional message."
(value) -> if value then value else panic! string ("Assert failed:", value)
(value) -> if value
then value
else panic! "Assert failed: {value}"
(msg, value) -> if value
then value
else panic! string ("Assert failed: ", msg, " with ", value)
else panic! "Assert failed: {msg} with {value}"
}
&&& Turtle & other graphics
@ -1198,8 +1186,6 @@ fn back! {
let bk! = back!
& turtles, like eveyrthing else in Ludus, use turns by default,
& not degrees
fn left! {
"Rotates the turtle left, measured in turns. Alias: lt!"
(turns as :number) -> add_command! ((:left, turns))
@ -1374,6 +1360,7 @@ pkg Prelude {
coll? & dicts lists sets tuples
colors & turtles
concat & string list set
contains? & list set
cos & math
count & string list set tuple dict
dec & math
@ -1430,6 +1417,8 @@ pkg Prelude {
max & math
min & math
mod & math
mod/0
mod/safe
mult & math
neg & math
neg? & math
@ -1439,6 +1428,7 @@ pkg Prelude {
odd? & math
ok & results
ok? & results
omit & set
or & bool
ordered? & lists tuples strings
p5_calls & turtles
@ -1455,7 +1445,6 @@ pkg Prelude {
pos? & math
position & turtles
print! & environment
& prn! & environment
pu! & turtles
pw! & turtles
rad/deg & math
@ -1480,6 +1469,8 @@ pkg Prelude {
some & values
some? & values
split & strings
sqrt & math
sqrt/safe & math
square & math
state & environment
store! & boxes