Compare commits

...

2 Commits

Author SHA1 Message Date
Scott Richmond
673a96ffb8 Update prelude 2024-01-22 17:17:29 -05:00
Scott Richmond
90c97e7cfe Fix exports & bump version 2024-01-19 17:49:26 -05:00
4 changed files with 88 additions and 22 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "@ludus/ludus-js-pure", "name": "@ludus/ludus-js-pure",
"version": "0.1.0-alpha.8", "version": "0.1.1",
"description": "A Ludus interpreter in a pure JS function.", "description": "A Ludus interpreter in a pure JS function.",
"main": "target/js/ludus.js", "main": "target/js/ludus.js",
"type": "module", "type": "module",

View File

@ -10,7 +10,7 @@
:modules {:main {:entries [ludus.node]}}} :modules {:main {:entries [ludus.node]}}}
:module {:target :esm :module {:target :esm
:output-dir "target/js" :output-dir "target/js"
:modules {:ludus {:exports {run ludus.node/run test ludus.node/run-test}}} :modules {:ludus {:exports {run ludus.node/run test ludus.node/doug}}}
} }
:browser {:target :browser :browser {:target :browser
:output-dir "target/js" :output-dir "target/js"

View File

@ -62,7 +62,7 @@
)) ))
) )
(defn test-run [source] (run source true)) (defn doug [source] (run source true))
(comment (comment

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)
@ -375,7 +377,7 @@ fn mult {
() -> 1 () -> 1
(x as :number) -> x (x as :number) -> x
(x as :number, y as :number) -> base :mult (x, y) (x as :number, y as :number) -> base :mult (x, y)
(x, y, ...zs) -> fold (base :mult, mult (x, y), zs) (x, y, ...zs) -> fold (base :mult, zs, mult (x, y))
(scalar as :number, (x, y)) -> (mult (x, scalar), mult (y, scalar)) (scalar as :number, (x, y)) -> (mult (x, scalar), mult (y, scalar))
((x, y), scalar as :number) -> mult (scalar, (x, y)) ((x, y), scalar as :number) -> mult (scalar, (x, y))
} }
@ -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