Compare commits

..

No commits in common. "5c037ed46e6f54ae3454c778bb7e58cdbf8414f5" and "f657da57eff50c546f75e3d4723acc0df4b4e2f1" have entirely different histories.

7 changed files with 27 additions and 125 deletions

View File

@ -1,33 +1,6 @@
set dotenv-load
# start a repl
repl:
clj -X:repl
default:
@just --list
test:
@echo $test
# build clojurescript release
build:
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

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

View File

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

View File

@ -664,21 +664,16 @@
)
))
(def state (atom {})) ;; for now use a global state atom
(defn- interpret-ref [ast ctx]
(let [data (:data ast)
name (-> data first :data first)
expr (-> data second)]
(when (contains? @ctx name)
(throw (ex-info (str "Name " name " is already bound") {:ast ast})))
(when (contains? @state name)
(throw (ex-info (str "A ref already has " name " name") {:ast ast})))
(let [value (interpret-ast expr ctx)
box (atom value)
ref {::data/ref true ::data/value box ::data/name name ::data/state state}]
ref {::data/ref true ::data/value box ::data/name name}]
(vswap! ctx update-ctx {name ref})
(swap! state assoc name ref)
ref)))
(defn- interpret-loop [ast ctx]
@ -817,7 +812,7 @@
(resolve-word times-expr ctx)
(-> times-expr :data first))
expr (second data)]
(when (not (number? times)) (throw (ex-info (str "Repeat needs a number, not a " (base/get-type times)) {})))
(if (not (number? times)) (throw (ex-info (str "Repeat needs a number, not a " (base/get-type times)) {})))
(dotimes [_ times] (interpret-ast expr ctx))))
(defn- interpret-literal [ast] (-> ast :data first))

View File

@ -43,7 +43,7 @@
post_tokens (:tokens post_scanned)
post_parsed (p/apply-parser g/script post_tokens)
post_result (i/interpret-safe source post_parsed {} false)
ludus_result (assoc post_result :result result_str :test test_results :state @i/state)
ludus_result (assoc post_result :result result_str :test test_results)
clj_result (ld->clj ludus_result)
]
(cond
@ -62,7 +62,7 @@
))
)
(defn doug [source] (run source true))
(defn test-run [source] (run source true))
(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)."
([]) -> true
(#{}) -> true
(s as :set) -> eq? (s, ${})
(s as :set) -> eq (s, ${})
(()) -> true
("") -> true
(_) -> false
@ -165,8 +165,6 @@ fn set? {
(_) -> false
}
& add a contains? or has? function
fn fold {
"Folds a list."
(f as :fn, xs as :list) -> fold (f, xs, f ())
@ -181,7 +179,7 @@ fn fold {
}
fn map {
"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]`."
"Maps over a list."
(f as :fn, xs) -> {
fn mapper (prev, curr) -> append (prev, f (curr))
fold (mapper, xs, [])
@ -193,7 +191,7 @@ fn map {
}
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. E.g., `filter ([1, 2, 3, 4], odd?) &=> [1, 3]`."
"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."
(p? as :fn, xs) -> {
fn filterer (filtered, x) -> if p? (x)
then append (filtered, x)
@ -415,20 +413,10 @@ 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 {
"Returns the absolute value of a number."
(0) -> 0
(n as :number) -> if neg? (n) then mult (-1, n) else n
(n) -> if neg? (n) then mult (-1, n) else n
}
fn neg {
@ -495,14 +483,6 @@ 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? {
"Returns true if a value is a negative number, otherwise returns false."
(x as :number) if lt? (x, 0) -> true
@ -517,30 +497,16 @@ fn pos? {
fn even? {
"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
}
fn odd? {
"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
}
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
fn keyword? {
"Returns true if a value is a keyword, otherwise returns false."
@ -624,7 +590,6 @@ fn or {
&&& 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."
() -> #{}
@ -712,7 +677,6 @@ fn assoc? {
(_) -> false
}
& TODO: consider merging `get` and `at`
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."
(key as :keyword) -> get (key, _)
@ -720,7 +684,6 @@ fn get {
(key as :keyword, coll, default) -> base :get (key, coll, default)
}
& 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, _)
@ -728,7 +691,8 @@ fn has? {
}
fn dict {
"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."
"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."
(struct as :struct) -> base :to_dict (struct)
(ns_ as :ns) -> base :to_dict (ns_)
(dict as :dict) -> dict
(list as :list) -> fold (assoc, list)
@ -865,25 +829,12 @@ fn dist {
}
&&& more number functions
& TODO: add max, min
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 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."
() -> base :random ()
(n as :number) -> base :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 {
@ -902,7 +853,7 @@ fn round {
}
fn range {
"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."
"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."
(end as :number) -> base :range (0, end)
(start as :number, end as :number) -> base :range (start, end)
}
@ -955,25 +906,15 @@ fn assert! {
&&& Turtle & other graphics
& some basic colors
& these are the "basic" css colors
& https://developer.mozilla.org/en-US/docs/Web/CSS/named-color
&&& TODO: add colors
let colors = #{
: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)
:light_gray (150, 150, 150, 255)
:dark_gray (50, 50, 50, 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)
:green (0, 255, 0, 255)
:blue (0, 0, 255, 255)
:teal (0, 128, 128, 255)
:aqua (0, 255, 25, 255)
:black (0, 0, 0, 255)
}
& the initial turtle state
@ -1045,7 +986,6 @@ fn render_turtle! () -> {
add_call! ((:vertex, x2, y2))
add_call! ((:vertex, x3, y3))
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! ((:line, 0, 0, x1, y1))
add_call! ((:pop))
@ -1279,8 +1219,6 @@ ns prelude {
div
div/0
div/safe
inv
inv/0
angle
abs
neg
@ -1293,9 +1231,6 @@ ns prelude {
gte?
lt?
lte?
min
max
between?
keyword?
nil?
some?
@ -1334,7 +1269,6 @@ ns prelude {
sum_of_squares
dist
random
random_int
pi
tau
floor