Compare commits

...

3 Commits

Author SHA1 Message Date
Scott Richmond
68aca09de6 updated janet-based docs 2024-06-15 17:04:01 -04:00
Scott Richmond
d94eb5485d fix some bugs, prep for doc 2024-06-15 17:03:49 -04:00
Scott Richmond
98d6b1c865 fix some bugs 2024-06-15 17:03:37 -04:00
4 changed files with 736 additions and 305 deletions

View File

@ -18,7 +18,7 @@ fn join
fn neg? fn neg?
fn atan/2 fn atan/2
fn mod fn mod
fn assoc? fn assoc & ?
fn dict fn dict
fn get fn get
fn unbox fn unbox
@ -370,14 +370,26 @@ fn ws? {
(_) -> false (_) -> false
} }
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
}
fn words { fn words {
"Takes a string and returns a list of the words in the string. Strips all whitespace." "Takes a string and returns a list of the words in the string. Strips all whitespace."
(str as :string) -> { (str as :string) -> {
let raw_strs = split (str, " ") let no_punct = strip (str)
fn joiner (list, str) -> if eq? (str, "") let strs = split (no_punct, " ")
fn worder (list, str) -> if empty? (str)
then list then list
else append (list, str) else append (list, str)
fold (joiner, raw_strs, []) fold (worder, strs, [])
} }
} }
@ -502,6 +514,11 @@ fn inv/0 {
(x as :number) -> div/0 (1, x) (x as :number) -> div/0 (1, x)
} }
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 { fn abs {
"Returns the absolute value of a number." "Returns the absolute value of a number."
(0) -> 0 (0) -> 0
@ -620,16 +637,13 @@ fn max {
& additional list operations now that we have comparitors & additional list operations now that we have comparitors
fn at { fn at {
"Returns the element at index n of a list or tuple. Zero-indexed: the first element is at index 0." "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."
(xs as :list, n as :number) -> when { (xs as :list, n as :number) -> base :nth (xs, n)
neg? (n) -> nil (xs as :tuple, n as :number) -> base :nth (n, xs)
gte? (n, count (xs)) -> nil (str as :string, n as :number) -> when {
true -> base :nth (n, xs) neg? (n) -> ""
} gte? (n, count (str)) -> ""
(xs as :tuple, n as :number) -> when { true -> base :slice (str, n, inc (n))
neg? (n) -> nil
gte? (n, count (xs)) -> nil
true -> base :nth (n, xs)
} }
(_) -> nil (_) -> nil
} }
@ -741,21 +755,17 @@ fn update {
} }
fn keys { fn keys {
"Takes an associative collection and returns a list of keys in that collection. Returns an empty list on anything other than a collection." "Takes a dict and returns a list of keys in that dict."
(coll) -> if not (assoc? (coll)) (dict as :dict) -> do dict > list > map (first, _)
then []
else do coll > list > map (first, _)
} }
fn values { fn values {
"Takes an associative collection and returns a list of values in that collection. Returns an empty list on anything other than a collection." "Takes a dict and returns a list of values in that dict."
(coll) -> if not (assoc? (coll)) (dict) -> do dict > list > map (second, _)
then []
else do coll > list > map (second, _)
} }
fn diff { fn diff {
"Takes two associate data structures and returns a dict describing their differences. Does this shallowly, offering diffs only for keys in the original dict." "Takes two dicts and returns a dict describing their differences. Does this shallowly, offering diffs only for keys in the original dict."
(d1 as :dict, d2 as :dict) -> { (d1 as :dict, d2 as :dict) -> {
let key1 = keys (d1) let key1 = keys (d1)
let key2 = keys (d2) let key2 = keys (d2)
@ -782,7 +792,7 @@ fn diff {
} }
fn coll? { fn coll? {
"Returns true if a value is a collection: dict, struct, list, tuple, or set." "Returns true if a value is a collection: dict, list, pkg, tuple, or set."
(coll as :dict) -> true (coll as :dict) -> true
(coll as :list) -> true (coll as :list) -> true
(coll as :tuple) -> true (coll as :tuple) -> true
@ -799,7 +809,7 @@ fn ordered? {
} }
fn assoc? { fn assoc? {
"Returns true if a value is an associative collection: a dict, struct, or namespace." "Returns true if a value is an associative collection: a dict or a pkg."
(assoc as :dict) -> true (assoc as :dict) -> true
(assoc as :pkg) -> true (assoc as :pkg) -> true
(_) -> false (_) -> false
@ -807,10 +817,10 @@ fn assoc? {
& TODO: consider merging `get` and `at` & 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 key, dict, and optional default value; returns the value at key. If the value is not found, returns nil or the default value."
(key as :keyword) -> get (key, _) (key as :keyword) -> get (key, _)
(key as :keyword, coll) -> base :get (key, coll) (key as :keyword, dict as :dict) -> get (key, dict)
(key as :keyword, coll, default) -> base :get (key, coll, default) (key as :keyword, dict as :dict, default) -> base :get (key, dict, default)
} }
& TODO: add sets to this? & TODO: add sets to this?
@ -1338,159 +1348,164 @@ fn penwidth {
box state = nil box state = nil
pkg Prelude { pkg Prelude {
abs abs & math
add add & math
and and & bool
angle angle & math
any? any? & dicts lists strings sets tuples
append append & lists sets
assert! assert! & errors
assoc assoc & dicts
assoc? assoc? & dicts
at at & lists strings
atan/2 atan/2 & math
back! back! & turtles
background! background! & turtles
between? between? & math
bg! bg! & turtles
bgcolor bgcolor & turtles
bk! bk! & turtles
bool bool & bool
bool? bool? & bool
box? box? & boxes
butlast butlast & lists strings tuples
ceil ceil & math
clear! clear! & turtles
coll? coll? & dicts lists sets tuples
colors colors & turtles
concat concat & string list set
cos cos & math
count count & string list set tuple dict
dec dec & math
deg/rad deg/rad & math
deg/turn deg/turn & math
dict dict & dict
dict? dict? & dict
diff diff & dict
dissoc dissoc & dict
dist dist & math
div div & math
div/0 div/0 & math
div/safe div/safe & math
doc! doc! & env
downcase downcase & string
each! each! & list
empty? empty? & list dict set string tuple
eq? eq? & values
err err & result
err? err? & result
even? even? & math
false? false? & bool
fd! fd! & turtles
filter filter & list
first first & list tuple
floor floor & math
fn? fn? & functions
fold fold & lists
forward! forward! & turtles
get get & dicts
goto! goto! & turtles
gt? gt? & math
gte? gte? & math
heading heading & turtles
heading/vector heading/vector & math
home! home! & turtles
inc inc & math
inv inv & math
inv/0 inv/0 & math
join inv/safe & math
keep join & lists strings
keys keep & lists
keyword? keys & dicts
last keyword? & keywords
left! last & lists tuples
list left! & turtles
load_turtle_state! list & lists
lt! list? & lists
lt? load_turtle_state! & turtles
lte? lt! & turtles
map lt? & math
max lte? & math
min map & lists
mod max & math
mult min & math
neg mod & math
neg? mult & math
neq? neg & math
nil? neg? & math
not neq? & values
odd? nil? & nil
ok not & bool
ok? odd? & math
or ok & results
ordered? ok? & results
p5_calls or & bool
pc! ordered? & lists tuples strings
pd! p5_calls & turtles
pencolor pc! & turtles
pencolor! pd! & turtles
pendown! pencolor & turtles
pendown? pencolor! & turtles
penup! pendown! & turtles
penwidth pendown? & turtles
penwidth! penup! & turtles
pi penwidth & turtles
pos? penwidth! & turtles
position pi & math
print! pos? & math
prn! position & turtles
pu! print! & environment
pw! & prn! & environment
rad/deg pu! & turtles
rad/turn pw! & turtles
random rad/deg & math
random_int rad/turn & math
range random & math dicts lists tuples sets
render_turtle! random_int & math
report! range & math lists
reset_turtle! render_turtle! & turtles
rest report! & environment
right! reset_turtle! & turtles
round rest & lists tuples
rt! right! & turtles
second round & math
set rt! & turtles
set? second & lists tuples
show sentence & lists strings
sin set & sets
slice set? & sets
some show & strings
some? sin & math
split slice & lists tuples strings
square some & values
state some? & values
store! split & strings
string square & math
string? state & environment
sub store! & boxes
sum_of_squares string & strings
tan string? & strings
tau strip & strings
trim sub & math
tuple? sum_of_squares & math
turn/deg tan & math
turn/rad tau & math
turtle_commands trim & strings
turtle_state tuple? & tuples
turtle_states turn/deg & math
type turn/rad & math
unbox turtle_commands & turtles
unwrap! turtle_state & turtles
unwrap_or turtle_states & turtles
upcase type & values
update unbox & boxes
update! unwrap! & results
values unwrap_or & results
words upcase & strings
zero? update & dicts
update! & boxes
values & dicts
words & strings lists
ws? & strings
zero? & math
} }

File diff suppressed because one or more lines are too long

View File

@ -130,8 +130,12 @@
(string/join (map (fn [x] (-> x first show-patt)) clauses) "\n")) (string/join (map (fn [x] (-> x first show-patt)) clauses) "\n"))
(defn doc [fnn] (defn doc [fnn]
(def {:name name :doc doc} fnn) (when (not= :fn (ludus/type fnn)) (break "No documentation available."))
(string/join [name (pretty-patterns fnn) doc] "\n")) (def {:name name :doc docstring} fnn)
(string/join [name
(pretty-patterns fnn)
(if docstring docstring "No docstring available.")]
"\n"))
(defn- conj!-set [sett value] (defn- conj!-set [sett value]
(set (sett value) true) (set (sett value) true)

127
src/doc.janet Normal file
View File

@ -0,0 +1,127 @@
(import /src/base :as base)
(import /src/prelude :as prelude)
(defn map-values [f dict]
(from-pairs (map (fn [[k v]] [k (f v)]) (pairs dict))))
(def with-docs (map-values base/doc prelude/ctx))
(def sorted-names (-> with-docs keys sort))
(defn escape-underscores [str] (string/replace "_" "\\_" str))
(defn escape-punctuation [str] (->> str
(string/replace "?" "")
(string/replace "!" "")))
(defn toc-entry [name]
(def escaped (escape-underscores name))
(string "[" escaped "](#" (escape-punctuation escaped) ")"))
(def alphabetical-list
(string/join (map toc-entry sorted-names) "    "))
(def topics {
"math" ["abs" "add" "angle" "atan/2" "between?" "ceil" "cos" "dec" "deg/rad" "deg/turn" "dist" "div" "div/0" "div/safe" "even?" "floor" "gt?" "gte?" "heading/vector" "inc" "inv" "inv/0" "inv/safe" "lt?" "lte?" "max" "min" "mod" "mult" "neg" "neg?" "odd?" "pi" "pos?" "rad/deg" "rad/turn" "random" "random_int" "range" "round" "sin" "square" "sub" "sum_of_squares" "tan" "tau" "turn/deg" "turn/rad" "zero?"]
"boolean" ["and" "bool" "bool?" "false?" "not" "or"]
"dicts" ["any?" "assoc" "assoc?" "coll?" "count" "dict" "dict?" "diff" "dissoc" "empty?" "get" "keys" "random" "update" "values"]
"lists" ["any?" "append" "at" "butlast" "coll?" "concat" "count" "each!" "empty?" "filter" "first" "fold" "join" "keep" "last" "list" "list?" "map" "ordered?""random" "range" "rest" "second" "sentence" "slice"]
"sets" ["any?" "append" "coll?" "concat" "count" "empty?" "random" "set" "set?"]
"strings" ["any?" "concat" "count" "downcase" "empty?" "join" "sentence" "show" "slice" "split" "string" "string?" "strip" "trim" "upcase" "words"]
"types and values" ["assoc?" "bool?" "coll?" "dict?" "eq?" "fn?" "keyword?" "list?" "neq?" "nil?" "number?" "ordered?" "show" "some" "some?" "string?" "type"]
"boxes and state" ["unbox" "store!" "update!"]
"results" ["err" "err?" "ok" "ok?" "unwrap!" "unwrap_or"]
"errors" ["assert!"]
"turtle graphics" ["back!" "background!" "bk!" "clear!" "colors" "fd!" "forward!" "goto!" "heading" "heading/vector" "home!" "left!" "lt!" "pc!" "pd!" "pencolor" "pencolor!" "pendown!" "pendown?" "penup!" "penwidth" "penwidth!" "position" "pu!" "pw!" "render_turtle!" "reset_turtle!" "right!" "rt!" "turtle_state"]
"environment and i/o" ["doc!" "print!" "report!" "state"]
})
(defn capitalize [str]
(def fst (slice str 0 1))
(def rest (slice str 1))
(def init_cap (string/ascii-upper fst))
(def lower_rest (string/ascii-lower rest))
(string init_cap lower_rest))
(defn topic-entry [topic]
(string "### " (capitalize topic) "\n"
(as-> topic _ (topics _) (slice _) (sort _) (map toc-entry _)
(string/join _ "    "))
"\n"))
(def by-topic (let [the-topics (-> topics keys sort)
topics-entries (map topic-entry the-topics)]
(string/join topics-entries "\n")))
(defn compose-entry [name]
(def header (string "### " name "\n"))
(def the-doc (get with-docs name))
(when (= "No documentation available." the-doc)
(break (string header the-doc "\n")))
(def lines (string/split "\n" the-doc))
(def description (last lines))
(def patterns (string/join (slice lines 1 (-> lines length dec)) "\n"))
(string header description "\n```\n" patterns "\n```\n"))
(compose-entry "update")
(def entries (string/join (map compose-entry sorted-names) "\n\n"))
(def doc-file (string
```
# Ludus prelude documentation
These functions are available in every Ludus script.
The documentation for any function can be found within Ludus by passing the function to `doc!`,
e.g., running `doc! (add)` will send the documentation for `add` to the console.
For more information on the syntax & semantics of the Ludus language, see [language.md](./language.md).
## A few notes
**Naming conventions.** Functions whose name ends with a question mark, e.g., `eq?`, return booleans.
Functions whose name ends with an exclamation point, e.g., `make!`, change state in some way.
In other words, they _do things_ rather than _calculating values_.
Functions whose name includes a slash either convert from one value to another, e.g. `deg/rad`,
or they are variations on a function, e.g. `div/0` as a variation on `div`.
**How entries are formatted.** Each entry has a brief (sometimes too brief!) description of what it does.
It is followed by the patterns for each of its function clauses.
This should be enough to indicate order of arguments, types, and so on.
**Patterns often, but do not always, indicate types.** Typed patterns are written as `foo as :bar`,
where the type is indicated by the keyword.
Possible ludus types are: `:nil`, `:boolean`, `:number`, `:keyword` (atomic values);
`:string` (strings are their own beast); `:tuple` and `:list` (indexed collections); `:set` (sets are specific),
`:dict` and `:ns` (associative collections); and `:ref` (references).
**Conventional types.** Ludus has two types based on conventions.
* _Result tuples._ Results are a way of modeling the result of a calculation that might fail.
The two possible values are `(:ok, value)` and `(:err, msg)`.
`msg` is usually a string describing what went wrong.
To work with result tuples, see [`unwrap!`](#unwrap) and [`unwrap_or`](#unwrap_or).
That said, usually you work with these using pattern matching.
* _Vectors._ Vectors are 2-element tuples of x and y coordinates.
The origin is `(0, 0)`.
Many math functions take vectors as well as numbers, e.g., `add` and `mult`.
You will see vectors indicated in patterns by an `(x, y)` tuple.
You can see what this looks like in the last clause of `add`: `((x1, y1), (x2, y2))`.
## Functions by topic
```
by-topic
```
## All functions, alphabetically
```
alphabetical-list
```
## Function documentation
```
entries
))
(spit "prelude.md" doc-file)