slosh container/complex data types
TODO
- char/char literal
- bytes
- vec
- numbers / number literals
- boolean literals
String
A string. Strings in slosh are UTF8 encoded and are composed of chars that are UTF grapheme clusters. Because of the encoding strings can be indexed but they must be traversed to find an index (i.e. they are indexed by 'chars' not bytes).
Read only string constants can be created with double quotes in the Reader. For instance: (def str-const "Some String Const")
A mutable string can be created with the 'str' form, for instance: (def string (str "Some val: " val))
Characters at index can be accessed with dot notation (see Vector).
Other string functions:
- str: concats all the values provided (as strings) to produce a new string (mutable)
- str-replace
- str-trim
- str-trim!
- str-contains
- str-push!
- str-map
- str-empty?
- str-starts-with
- str-split
- char-whitespace?
Vector
A vector (dynamic array) of values. Typically a vector will be created with the '[' reader macro (expands to (vec ...)) for instance: (def vector[1 2 3]) Vectors can be indexed with 'dot' notation (dot is a reader macro that expends to a (get x index) for x.index. For example: (let (v [1 2 3]) (prn v.0 ", " v.1 ", " v.2)) Dot notation can also be used with set! to set elements: (let (v [1 2 3]) (set! v.0 10) (set! v.1 20) (set! v.2 30) (prn v.0 ", " v.1 ", " v.2))
Other vector functions:
- vec: longform for '[]' syntax, prefer using brackets
- make-vec: takes a capacity and default value, makes a vector of that size with all values set to default
- vec-push!: destructive, pushes a new value to the end of a vec (increases len by one)
- vec-pop!: destructive, pops the last value from a vector and returns it (decreases vector len by one)
- vec-slice: takes a vector, start index and optional end index (defaults to end of vector), returns a new vec of elements start (inclusive) end (exclusive)
HashMap
A map of key/value pairs. Use the '{' reader macro to create a hashmap: (def hm {:x 1, :y 2, :z 3}) Use dot notation (see vectors) to access and set keys: (let (hm {:x 1, :y 2, :z 3}) (set! hm.:x 10) (set! hm.:y 20) (set! hm.:z 30) (prn hm.:x ", " hm.:y ", " hm.:z))
Other hashmap function:
- make-hash: longform for '{}' reader macro, prefer '{}'
Pair/ConsCell/List
This is a traditional Lisp conscell data type, pair of values (car, cdr). It can be used to create a linked list of values.
Other Pair function:
- car
- cdr
- list
- list-append
- cons
- xar!
- xdr!
Common functions
These should all work on any of the containers.
- len: return the length of the container
- clear!: destructive form to remove all elements from the container
- set!: with dot notation to set an element of a container (see Vector and HashMap)
- dot notation: this is a reader macro the expends to (get val index) for val.index
Equality
The most common way to test equality is with =
For numeric equality (IEEE) use ==
For bytewise equality use identical?
The behavior and names are based on Clojure's implementation. Read their docs here: https://clojure.org/guides/equality
Also check out slosh/tests/equality.slosh
for some examples.
-
=
-
(= 2 0x2)
istrue
(comparing an int to a byte) -
(= 2 2.0)
isfalse
(comparing an int to a float) -
(= 0.0 -0.0)
istrue
-
(= NaN NaN)
isfalse
-
state.rs
defines special forms object with keyequal
mapped to the name=
. -
compile.rs
matches onenv.specials().equal
and generates opcodeEQUAL
-
exec_loop.rs
maps opcodeEQUAL
to functionis_equal
-
vm.rs
is_equal
converts each arg to aValue
(in case it needs to be dereferenced) and callsis_equal_pair
-
vm.rs
is_equal_pair
does a complex test for equality- check if both args are Byte or Int and if so, converts both to
i64
withValue::get_int
and compares with rust native==
- check if both args are numbers (Byte, Int, or Float) and if so, converts both to
f64
withValue::get_float
and compares with rust native==
- check if both args are Byte or Int and if so, converts both to
-
-
==
-
(== 1 1.0)
istrue
(comparing an int to a float) -
(== 0.0 -0.0)
istrue
-
(== NaN NaN)
isfalse
-
returns true whenever
=
does, but also returns true for numbers that are numerically equal -
when comparing two floats, converts two both to
f64
and compares with native f64==
-
does not use F56::PartialEq implementation
-
state.rs
defines special forms object with keynumeq
mapped to the name==
. -
compile.rs
callscompile_list
which callscompile_special
which callscompile_math
incompile_math.rs
-
compile_math.rs
compile_math
matches onenv.specials().numeq
and generates opcodeNUMEQ
-
exec_loop.rs
maps opcodeNUMEQ
to functioncompare_numeric
and passes a comparator|a,b| a == b
-
macros.rs
compare_numeric
- checks if either argument is a
Float
and if so, converts both tof64
withget_primitive_float
macro and uses the comparator - checks if either argument is a
Int
and if so, converts both toi64
withget_primitive_int
macro and uses the comparator
- checks if either argument is a
-
-
identical?
-
(identical? 1 1)
istrue
-
(identical? 1 1.0)
isfalse
(different types) -
(identical? 0.0 -0.0)
isfalse
(comparing floats with different bit patterns) -
(identical? NaN NaN)
might betrue
orfalse
. There are trillions of different bit patterns that represent NaN in IEEE 754 -
is the only equality comparison that uses
Value::PartialEq
implementation which is always false for different types of Values -
using identical equality for floats causes problems with hashing. #125 identical equality is 'too strict' in that you probably expect that +0 and -0 should hash to the same thing, but they don't rendering hash tables
-
state.rs
defines special forms object with keyeq
mapped to the nameidentical?
. -
compile.rs
matches onenv.specials().eq
and generates opcodeEQ
-
exec_loop.rs
maps opcodeEQ
to functionis_identical
-
vm.rs
is_identical
converts each arg to aValue
(in case it needs to be dereferenced) and comparesval1 == val2
which usesValue::PartialEq
implementation
-
-
assert-equal
- based on
=
- is a macro defined in core.slosh which checks if the arguments are
=
and throws an error if they are not
- based on
-
not=
- defined in
vm/core.slosh
as the negation of=
- defined in
-
not==
- defined in
vm/core.slosh
as the negation of==
- defined in
Namespaces
Description
Namespaces are compiler bookkeeping for organizing global symbols. When in a namespace then any symbols that are defined will have the current NAMESPACE:: prepended to the symbol. When symbols are resolved the compiler will also try to prepend the current namespace first in order to find the symbol.
Entering a namespace
From code use the 'with-ns' form.
(doc 'with-ns)
;; => "Usage: (with-ns SYMBOL sexp+)
;;
;; Create a namespace and compile sexp+ within it. Restore the previous namespace when scope ends.
;; THe symbol "::" will return to the "root" namespace (i.e. no namespace prepended to globals).
;; This will cause all globals defined to have namespace:: prepended.
;; This will also clear any existing imports.
;;
;; Section: core
;;
;; Example:
;; (with-ns test-with-ns
;; (def ttf (fn () '(1 2 3)))
;; (test::assert-equal '(1 2 3) (ttf))
;; (test::assert-equal '(1 2 3) (test-out::ttf)))
;; (test::assert-equal '(1 2 3) (test-out::ttf))
;; "
From the top-level REPL you can use 'ns'.
(doc 'ns)
;; => "Usage: (ns SYMBOL)
;;
;; Changes to namespace. This is "open-ended" change and is intended for use with
;; the REPL prefer with-ns for scripts.
;; The symbol "::" will return to the "root" namespace (i.e. no namespace prepended to globals).
;; This will cause all globals defined to have namespace:: prepended.
;; This will also clear any existing imports.
;;
;; Section: core
;;
;; Example:
;; (ns testing)
;; (def x #t)
;; (test::assert-true x)
;; (ns ::)
;; (test::assert-true testing::x)
;; "
This is an open-ended namespace change intended for the repl, prefer with-ns for a scoped namespace in code.
Imports
Other namespaces can be imported to allow its symbols to be accessed in a shorter form. Use the 'import' form for this .
(doc 'import)
;; => "Usage: (import namespace [:as symbol])
;;
;; Will import a namespace. Without an :as then all symbols in the namespace will become available in the current
;; namespace as if local. With [:as symbol] then all namespace symbols become available with symbol:: prepended.
;;
;; Section: core
;;
;; Example:
;; (ns testing)
;; (def x #t)
;; (test::assert-true x)
;; (ns ::)
;; (test::assert-true testing::x)
;; (import testing)
;; (test::assert-true x)
;; (import testing :as t)
;; (test::assert-true t::x)
;; "
For instance using (import iter)
will allow any symbols in the iter namespace to be used without prepending 'iter::'. You can also use the (import iter :as i)
, the :as form allows the namespace to be given a different name. In this case the iter namespace could be replaced with 'i', (i::for ...) instead of (iter::for ...) for example. Imports are resolved in the order they are compiled in case of conflict (i.e. the first import that resolves a symbol wins). Imports are attached to the current namespace, changing namespaces will clear imports (note that 'with-ns' saves and restores the previous namespace with imports).
Loading code
To load new code into your environment use load or run-script.
Load
The load form should generally be preferred. It will compile the code at compile time (vs runtime) and execute it at runtime. This means:
- The path parameter has to be known at compile time: a string const, defined global or form that does not need local inputs.
- Any symbols defined in the loaded code will be known to the compiler at compile time and available for use.
(doc 'load)
;; => "Usage: (load path) -> [last form value]
;;
;; Read and eval a file (from path- a string). The load special form executes at compile time.
;; This means it's parameter must resolve at compile time. Most of the time you will want to use
;; this in conjunction with 'with-ns' to namespace the contents.
;; Note: on it's own does nothing with namespaces.
;;
;; Section: core
;;
;; Example:
;; (comp-time (def test-temp-file (get-temp-file)) nil)
;; (defer (fs-rm test-temp-file))
;; (let (tst-file (fopen test-temp-file :create))
;; (defer (fclose tst-file))
;; (fprn tst-file "(with-ns test-load")
;; (fprn tst-file " (defn test-fn () '(1 2 3)))"))
;; (load test-temp-file) ; put stuff in it's own namespace
;; (test::assert-equal '(1 2 3) (test-load::test-fn))
;;
;;
;; (with-ns test-out2
;; (comp-time
;; (def test-temp-file (get-temp-file))
;; (let (tst-file (fopen test-temp-file :create))
;; (defer (fclose tst-file))
;; (fprn tst-file "(defn test-fn () '(1 2 3))"))
;; nil)
;; (defer (fs-rm test-temp-file))
;; (load test-temp-file) ; put new stuff in current namespace
;; (test::assert-equal '(1 2 3) (test-fn))
;; (test::assert-equal '(1 2 3) (test-out2::test-fn)))
;; "
Run Script
The run-script form loads each form in the file, compiles and executes it at runtime. This means:
- It can take any parameter since it is resolved at runtime.
- Globals it defines will NOT be known until after it runs at runtime.
(doc 'run-script)
;; => "Usage: (run-script path) -> [last form value]
;;
;; Read and eval a file (from path- a string).
;;
;; Section: scripting
;;
;; Example:
;; (def test-load::test-fn)
;; (with-temp-file (fn (tmp)
;; (let (tst-file (fopen tmp :create))
;; (defer (fclose tst-file))
;; (fprn tst-file "(with-ns test-load")
;; (fprn tst-file " (defn test-fn () '(1 2 3)))"))
;; (run-script tmp)
;; (test::assert-equal '(1 2 3) (test-load::test-fn))))
;; "
ERRORS
Error Type
The error type consists of an identifying keyword and a data/payload value. This value is typically a string but can be any valid value.
(type [error]) returns :Error The form (car [error]) returns the keyword identifying this error. The form (cdr [error]) returns the data value for the error. Note the use of car/cdr to pull apart an error even though it is not of the pair type. Use (mk-err :[ID] value) to create an error type or (err :[ID] value) to "raise" an error (see below).
Raising an error
Runtime errors will be "raised". This means the program execution will halt and the debugger will be entered. Currently this only allows examining the running state but will eventually include the ability to modify state and restart as other Lisps allow. Code can raise an error with the err form, for instance (err :some-error-type "This is my error") will raise an error and interrupt the program.
Note, the (get-error FORM+) form can be used to programmatically return a raised error instead of breaking to the debugger.
Returning an error
Code can return an error instead of breaking into the debugger. Use the (mk-err :[ERROR ID] vallue) to create an error and then use it as any other value (return it from a function for instance). This may be appropriate for a common error that does not warrent breaking to the debugger.
References
See the docs string for:
- err
- mk-err
- err?
- ok?
- get-error
Slosh/Lisp syntax and macros
The core unit of a Lisp is a form:
42
;; => 42
+
;; => #<SpecialFn(+)>
(list 1 2)
;; => (1 2)
;; The quote symbol means return the literal form after the character
;; This is identical to the form above.
'(1 2)
;; => (1 2)
are forms. All lisp code is structured as a sequence of forms inside forms separated by whitespace.
Expressions are enclosed in parentheses and Lisp uses prefix notation to parse expressions.
A parenthesized expression is a list of forms: (list 1 2 3 4)
where the first
form, the head, should generally be a function or a macro and dictates what the expression will do,
and the subsequent children are arguments to the head.
Comments
Single-line comments start with a ;
character and continue till the end of the line
; (prn "hello")
(prn "world")
;; => "world"
Multi-line comments start with the #|
characters to start and ends with the sample symbols reversed |#
.
#|
(prn "oh")
(prn "hello")
|#
(prn "world")
;; => "world"
Single forms can be commented out with #;
characters. This directive tells the reader to discard the form.
#;(prn "oh")
#;(prn "hello")
(prn "world")
;; => "world"
Documentation
Any symbol that is used with def
or defn
can also bind documentation. Documentation
is multiline and is delimited with #%
characters to start and ends with the same symbols reversed %#
.
#%
Usage: (login SECRET)
Returns true if logged in false if not. Must use `SECRET`
Section: secrets
Example:
(assert-false (login "foo"))
%#
(defn login (secret)
(if (= secret "helloworld")
#t
#f))
#%
Usage: (login SECRET)
Returns documentation for given symbol as map. Keyword is a documentation fragment
(usage, section, description, example) and value is text describing given fragment.
Section: secrets
Example:
(assert-false (login SECRET))
%#
(def SECRET "helloworld")
(prn (login SECRET))
;; => "true"
Macros
Helper macros syntax exists like in other lips such as:
quote ('
),
quasiquote (`
),
unquote (~
),
unquote-splice (~@
),
macro, and
defmacro.
(defmacro dotimes
;; This macro accepts times, some number, as well as some number of forms to be evaluated.
(times & body)
;; To avoid creating a variable name in the existing scope (gensym helps
;; with the creation of hygenic macros in slosh) is used and
;; the symbol i-name is assigned some random name.
(let (i-name (gensym))
;; The quasiquote is used within the body of the macro to specify that all forms
;; inside the parentheses should have an implicit quote in front of them. This
;; way, by default, the macro will return an ast with the literal forms, as opposed to the
;; evaulauted forms; forms can be evaluated in the resultant ast with the unquote.
`(let (~i-name 0)
;; i-iname and times are unquoted so they are evaluated ("escaping the
;; quasiquote") in the resultant ast returning numerical values instead
;; of symbols that evaluate to themselves.
(while (< ~i-name ~times)
;; Since body is a list, it utilizes the unquote-splice operator, to
;; expand each of its elements into the resultant ast, in the case
;; of dotimes `body` in intended to be some number of forms that
;; should be executed on each invocation.
~@body
;; because i-name is a symbol defined in the outer scope use unquote
;; so the resultant ast outputs `(inc! random-var-nmae)` rather than
;; (inc! i-name). `i-name` is not a symbol in scope in the resultant ast,
;; ~i-name evaluates to a symbol that is in scope in the resultant
;; ast.
(inc! ~i-name)))))
NOTE: The quasiquote is a quality of life macro helper, that obviates the need to add a single quote to every list and every form in the body of your macro. It is particularly useful in macros because often times many of the symbols you want returned in a macro are the literal symbols you want in the output ast.
Iterators
Still figuring this out currently allows things like this (syntax will probably change):
Current iteration uses :< (same as :0<) to connect a file stdin and :> (same as :1>) to connect a file to stdout. These can also be used with file descriptors (for example :2> will connect a file to stderr). They can be mixed with other shell redirects and will just become part of the redirect stack. Each lisp redirect will return a file in a list (first element will be the PID of the final process).
Examples: (let ([pid, out] (sh "ls" :>)) (iter::for l in (iter::iter out) (pr l)))
(let ([pid, out, er] (sh "ls vm/src/ sdsdf" :> :2>))(prn "PID: " pid) (iter::for l in (iter::iter out)(pr "from out: " l))(iter::for l in (iter::iter er)(pr "from err: " l)
Set the lisp pipe then use a shell redirect to also send stderr to the same pipe: (let ([pid, out] (sh "ls vm/src/ sdsdf" :> "2>&1"))(prn "PID: " pid) (iter::for l in (iter::iter out)(pr "from grep: " l)))
(let ([pid, in, out] (sh :< "grep XX" :>))(prn "PID: " pid) (fprn in "XXsls")(fprn in "sls")(fprn in "dfdXX")(fclose in)(iter::for l in (iter::iter out)(pr "from grep: " l)))
(same as above but include a pipe between shell commands) (let ([pid, in, out] (sh :< "grep XX | cat -" :>)) (fprn in "XXsls")(fprn in "sls")(fprn in "dfdXX")(fclose in)(iter::for l in (iter::iter out)(pr l)))
LET Bindings
Basic form
(let ([name value]) forms)
Let will create a new lexical scope and will bind names to local variables with provided values. After the binding it is an implicit do form. The bound symbols will only be in scope within this implicit do. Afterwards they will be unbound or will revert to their previous shadowed value.
Note all bindings are required to be pairs (name value), name is a symbol and value can be any form (it will be evaluated).
Let will shadow any variable names from outer scopes (including globals), it does not interact with dynamic scopes at all. Let is similar to let-rec in scheme, the bindings are created in order and later binding can see the values of previous bindings. It will also allow an early reference to "see" a later binding (like let-rec) allowing some recursive forms to be bound easily. Note that when doing this the later form must NOT be something being shadowed or the existing binding will be used not the new one in the let.
Examples
(let (a 1, b 2, c 3) `(~a ~b ~c))
;; => (1 2 3)
Produces (1 2 3)
(let (a 1, b 2, c 3) (let (b 20, c (+ b 10)) `(~a ~b ~c)))
;; => (1 20 30)
Produces (1 20 30)
(let (a 1, b 2, c 3) (let (x (+ b 1), b 20, c (+ b 10)) `(~a ~x ~b ~c)))
;; => (1 3 20 30)
Produces (1 3 20 30)
(let (fnx (fn (x) (if (= x 0) #t (fny (- x 1))))
fny (fn (y) (if (= y 0) #t (fnx (- y 1)))))
(fnx 10))
;; => true
Example of recursive references (a dumb one). It will produce #t (true) after ping ponging between fnx and fny.
(let (fny (fn (y) y))
(let (fnx (fn (x) (if (= x 0) #t (fny (- x 1))))
fny (fn (y) (if (= y 0) #t (fnx (- y 1)))))
(fny 10)))
;; => 8
This example will produce 8 because fnx will use the outer fny instead of the next fny.
Destructure bindings
Let supports destructure bindings of sequences (list, vector) and hashmaps. It can support optional bindings as well as rest (&) for sequences.
For sequences use [name+], if & is before the last name then it will get all the leftover values. A % indicates that all the names after are optional (default to nil) and you can use := to set the default value. Patterns must match exactly, for instance [a b c] requires a sequence with exactly three elements. [a b c & rest] requires a sequence with at least three elements. [% a b c & rest] will take any sequence and bind the first, second and third values to a b and c if available.
For maps use {[name key]+} (i.e. a map of symbols to keys), if this map contains :or then it's value will be a map of key to default value used for any missing keys. It requires all keys to be included in the destructured map or to have a default.
Note that destructures can be applied recursively and sequence destructure can contain map destructures and vice versa.
Examples
(def x '(1 2 3))
(let ([a b c] x) `(~a ~b ~c))
;; => (1 2 3)
Produces (1 2 3).
(let ([a b % c d] '(1 2)) (list a b c d))
;; => (1 2 nil nil)
Produces (1 2 nil nil).
(let ({a :one, b 'two, c "three" [d e] :vec} {:one 1, 'two 2, "three" 3, :vec [4 5]}) (list a b c d e))
;; => (1 2 3 4 5)
Produces (1 2 3 4 5).
Namespaces
Description
Namespaces are compiler bookkeeping for organizing global symbols. When in a namespace then any symbols that are defined will have the current NAMESPACE:: prepended to the symbol. When symbols are resolved the compiler will also try to prepend the current namespace first in order to find the symbol.
Entering a namespace
From code use the 'with-ns' form.
(doc 'with-ns)
;; => "Usage: (with-ns SYMBOL sexp+)
;;
;; Create a namespace and compile sexp+ within it. Restore the previous namespace when scope ends.
;; THe symbol "::" will return to the "root" namespace (i.e. no namespace prepended to globals).
;; This will cause all globals defined to have namespace:: prepended.
;; This will also clear any existing imports.
;;
;; Section: core
;;
;; Example:
;; (with-ns test-with-ns
;; (def ttf (fn () '(1 2 3)))
;; (test::assert-equal '(1 2 3) (ttf))
;; (test::assert-equal '(1 2 3) (test-out::ttf)))
;; (test::assert-equal '(1 2 3) (test-out::ttf))
;; "
From the top-level REPL you can use 'ns'.
(doc 'ns)
;; => "Usage: (ns SYMBOL)
;;
;; Changes to namespace. This is "open-ended" change and is intended for use with
;; the REPL prefer with-ns for scripts.
;; The symbol "::" will return to the "root" namespace (i.e. no namespace prepended to globals).
;; This will cause all globals defined to have namespace:: prepended.
;; This will also clear any existing imports.
;;
;; Section: core
;;
;; Example:
;; (ns testing)
;; (def x #t)
;; (test::assert-true x)
;; (ns ::)
;; (test::assert-true testing::x)
;; "
This is an open-ended namespace change intended for the repl, prefer with-ns for a scoped namespace in code.
Imports
Other namespaces can be imported to allow its symbols to be accessed in a shorter form. Use the 'import' form for this .
(doc 'import)
;; => "Usage: (import namespace [:as symbol])
;;
;; Will import a namespace. Without an :as then all symbols in the namespace will become available in the current
;; namespace as if local. With [:as symbol] then all namespace symbols become available with symbol:: prepended.
;;
;; Section: core
;;
;; Example:
;; (ns testing)
;; (def x #t)
;; (test::assert-true x)
;; (ns ::)
;; (test::assert-true testing::x)
;; (import testing)
;; (test::assert-true x)
;; (import testing :as t)
;; (test::assert-true t::x)
;; "
For instance using (import iter)
will allow any symbols in the iter namespace to be used without prepending 'iter::'. You can also use the (import iter :as i)
, the :as form allows the namespace to be given a different name. In this case the iter namespace could be replaced with 'i', (i::for ...) instead of (iter::for ...) for example. Imports are resolved in the order they are compiled in case of conflict (i.e. the first import that resolves a symbol wins). Imports are attached to the current namespace, changing namespaces will clear imports (note that 'with-ns' saves and restores the previous namespace with imports).
Loading code
To load new code into your environment use load or run-script.
Load
The load form should generally be preferred. It will compile the code at compile time (vs runtime) and execute it at runtime. This means:
- The path parameter has to be known at compile time: a string const, defined global or form that does not need local inputs.
- Any symbols defined in the loaded code will be known to the compiler at compile time and available for use.
(doc 'load)
;; => "Usage: (load path) -> [last form value]
;;
;; Read and eval a file (from path- a string). The load special form executes at compile time.
;; This means it's parameter must resolve at compile time. Most of the time you will want to use
;; this in conjunction with 'with-ns' to namespace the contents.
;; Note: on it's own does nothing with namespaces.
;;
;; Section: core
;;
;; Example:
;; (comp-time (def test-temp-file (get-temp-file)) nil)
;; (defer (fs-rm test-temp-file))
;; (let (tst-file (fopen test-temp-file :create))
;; (defer (fclose tst-file))
;; (fprn tst-file "(with-ns test-load")
;; (fprn tst-file " (defn test-fn () '(1 2 3)))"))
;; (load test-temp-file) ; put stuff in it's own namespace
;; (test::assert-equal '(1 2 3) (test-load::test-fn))
;;
;;
;; (with-ns test-out2
;; (comp-time
;; (def test-temp-file (get-temp-file))
;; (let (tst-file (fopen test-temp-file :create))
;; (defer (fclose tst-file))
;; (fprn tst-file "(defn test-fn () '(1 2 3))"))
;; nil)
;; (defer (fs-rm test-temp-file))
;; (load test-temp-file) ; put new stuff in current namespace
;; (test::assert-equal '(1 2 3) (test-fn))
;; (test::assert-equal '(1 2 3) (test-out2::test-fn)))
;; "
Run Script
The run-script form loads each form in the file, compiles and executes it at runtime. This means:
- It can take any parameter since it is resolved at runtime.
- Globals it defines will NOT be known until after it runs at runtime.
(doc 'run-script)
;; => "Usage: (run-script path) -> [last form value]
;;
;; Read and eval a file (from path- a string).
;;
;; Section: scripting
;;
;; Example:
;; (def test-load::test-fn)
;; (with-temp-file (fn (tmp)
;; (let (tst-file (fopen tmp :create))
;; (defer (fclose tst-file))
;; (fprn tst-file "(with-ns test-load")
;; (fprn tst-file " (defn test-fn () '(1 2 3)))"))
;; (run-script tmp)
;; (test::assert-equal '(1 2 3) (test-load::test-fn))))
;; "
End to End example of Slosh execution
- Start with the lisp program
(1.1)
- In
reader.rs
- The
read_inner
function controls most parsing.- Numeric parsing happens at the end in the catch-all of the match statement.
- The
do_atom
function attempts to parse 1.1 as an i64 and fails, so it parses it as f64 and then calls.into()
to convert to aValue
- In
main.rs
- The
exec_expression
function callspass1
- And then it calls
compile
- In
pass1.rs
- The
pass1
function initially operates on (1.1) as a pair or list - It iterates over each element of the list and recursively calls
pass1
on each element - So then
pass1
is called on 1.1 which is then handled in the catch-all of the match statement - and this is where we add 1.1 to the heap and add it is a constant to the vm
- In
state.rs
add_constant
is called which inserts theValue
of 1.1 intopub struct CompileState
'spub constants: HashMap<Value, usize>
- Since constants are stored in a hashmap, if two different numeric constants hash to the same thing, they will be stored as the same constant
- In
float_56.rs
F56
impl's theHash
trait and has a custom implementation ofhash
that converts theF56
to au64
and then hashes the result
- In
compile.rs
- Recall that
exec_expression
calledcompile
afterpass1
- calls to
compile
trickle down intocompile_list
,compile_special
,compile_math
, and others. compile_list
handles a single value like this and the match statement catch-all prints Boo and the value itself
Slosh Forms
List of sections:
char, collection, conditional, conversion, core, doc, file, hashmap, io, iterator, math, pair, random, scripting, sequence, shell, string, system, test, type, vector
Section: char
char-lower, char-upper, char-whitespace?, str->int
Section: collection
clear!, empty?, flatten, in?, not-empty?, reverse
Section: conditional
<, <=, ==, >, >=, and, cond, if, match, not, or, when
Section: conversion
Section: core
=, apply, back-quote, block, comp-time, dec!, def, defmacro, defn, do, doc, dotimes, dotimes-i, dyn, err, eval, fn, get-error, get-globals, get-in-namespace, get-namespaces, identity, import, inc!, len, let, let-while, load, loop, macro, mk-err, not=, not==, ns, nsubstitute!, occurs, on-raised-error, quote, recur, set!, substitute, to-list, to-vec, usage, with-ns
Section: doc
build-doc, doc-map, get-exemptions, get-globals-sorted, legacy_forms
Section: file
cd, fclose, fflush, fopen, fs-accessed, fs-base, fs-crawl, fs-dir?, fs-exists?, fs-file?, fs-len, fs-modified, fs-parent, fs-rm, fs-same?, get-temp, get-temp-file, glob, read, read-all, read-line, temp-dir, with-temp, with-temp-file
Section: hashmap
hash-keys, hash-remove!, make-hash
Section: io
Section: iterator
iter::enumerate, iter::file-iter, iter::filter, iter::for, iter::iter, iter::iter-or-single, iter::iter?, iter::list-iter, iter::map, iter::mk-iter, iter::once-iter, iter::range, iter::reduce, iter::repeat-iter, iter::string-iter, iter::vec-iter, iter::vec-iter-pair, iter::vec-iter-rev
Section: math
%, *, +, -, /, abs, rem, rem_euclid
Section: pair
Section: random
Section: scripting
Section: sequence
butlast, first, last, rest, seq-for
Section: shell
Section: string
str, str-bytes, str-cat-list, str-contains, str-empty?, str-lower, str-map, str-push!, str-replace, str-split, str-splitn, str-starts-with, str-sub, str-trim, str-trim!, str-upper
Section: system
Section: test
test::assert-equal, test::assert-error, test::assert-error-msg, test::assert-false, test::assert-not-equal, test::assert-true
Section: type
callable?, char?, err?, io?, list?, nil?, ok?, pair?, seq?, str->float, string?, symbol?, vec?
Section: vector
make-vec, vec, vec->list, vec-pop!, vec-push!, vec-slice
char
List of symbols:
char-lower, char-upper, char-whitespace?, str->int
char-lower
Usage: (char-lower char) -> char
Get lower case (utf) string for a character.
Example:
(test::assert-equal "a" (char-lower \A))
(test::assert-equal "a" (char-lower \a))
(test::assert-not-equal "a" (char-lower \Z))
(test::assert-equal "λ" (char-lower \Λ))
(test::assert-equal "λ" (char-lower \λ))
(test::assert-equal "ß" (char-lower \ß))
char-upper
Usage: (char-upper char) -> char
Get upper case (utf) string for a character.
Example:
(test::assert-equal "A" (char-upper \A))
(test::assert-equal "A" (char-upper \a))
(test::assert-not-equal "A" (char-upper \Z))
(test::assert-equal "Λ" (char-upper \λ))
(test::assert-equal "Λ" (char-upper \Λ))
;; "the" exception and a reason for returning a string
(test::assert-equal "SS" (char-upper \ß))
char-whitespace?
Usage: (char-whitespace? char) -> t/nil
Returns true if a character is whitespace, false/nil otherwise.
Example:
(test::assert-true (char-whitespace? \ ))
(test::assert-true (char-whitespace? \tab))
(test::assert-false (char-whitespace? \s))
str->int
Usage: (str->int string) -> int
If string is a valid representation of an integer return that int. Error if not.
Example:
(test::assert-equal 0 (str->int "0"))
(test::assert-equal 101 (str->int "101"))
(test::assert-equal -101 (str->int "-101"))
(test::assert-error (str->int "not int"))
(test::assert-error (str->int "10.0"))
(test::assert-error (str->int "--10"))
collection
List of symbols:
clear!, empty?, flatten, in?, not-empty?, reverse
clear!
Usage: (clear! container)
Clears a container (vector, hash-map, string). This is destructive!
Example:
(def test-clear-vec (vec 1 2 3))
(test::assert-false (empty? test-clear-vec))
(clear! test-clear-vec)
(test::assert-true (empty? test-clear-vec))
empty?
Usage: (empty? v)
Usage (empty? s)
No Examples
flatten
Usage: (flatten & rest)
Takes a sequence composed of individual values or sequences of values and turns it into one vector of values.
Example:
(test::assert-equal [1 2 3 1 2 3] (flatten 1 2 3 (list 1 2 3)))
(test::assert-equal [1 2 3 1 2 3] (flatten 1 2 3 [1 2 3]))
(test::assert-equal [1 2 3 1 2] (flatten 1 2 3 (list 1 2)))
(test::assert-equal [1 2 3 1 2 3 1 2] (flatten 1 2 3 (list 1 2 3 (list 1 2))))
in?
Usage: (in? needle haystack)
In provided sequence, haystack, find a specific value, needle.
Example:
(test::assert-true (in? [1 2 3 4 5] 3))
(test::assert-false (in? [1 2 3 4 5] 9))
(test::assert-true (in? (list 1 2 3 4 5) 3))
(test::assert-true (in? '(1 2 3 4 5) 5))
not-empty?
Usage: (not-empty? v)
Usage (not-empty? s)
No Examples
reverse
Usage: (reverse items)
Produce a vector that is the reverse of items.
Example:
(let (tmap [1 2 3 0])
(test::assert-false (empty? tmap))
(set! tmap (reverse tmap))
(test::assert-equal 2 (get tmap 2))
(test::assert-equal 1 (get tmap 3))
(test::assert-equal 0 (get tmap 0))
(test::assert-error (reverse "string")))
conditional
{{ #include section-docs/conditional.md }}
List of symbols:
<, <=, ==, >, >=, and, cond, if, match, not, or, when
<
Usage: (< val0 ... valN)
Less than. Works for int, float or string.
Example:
(test::assert-true (< 1 2))
(test::assert-true (< 1 2 3 4))
(test::assert-false (< 2 2))
(test::assert-false (< 2 2 2))
(test::assert-false (< 2 2 3))
(test::assert-true (< 1.0 2.0))
(test::assert-false (< 2.0 2.0))
(test::assert-false (< 2.0 2.0 2.0))
(test::assert-false (< 2.0 2.0 3.0))
(test::assert-false (< 2.1 2.0 3.0))
(test::assert-false (< 2 1))
(test::assert-false (< 3 2 3))
(test::assert-true (< 1.0 1.1 ))
(test::assert-true (< 1.0 1.01 ))
(test::assert-true (< 1.0 1.001 ))
(test::assert-true (< 1.0 1.0001 ))
(test::assert-true (< 1.0 1.00001 ))
(test::assert-true (< 1.0 1.000001 ))
(test::assert-true (< 1.0 1.0000001 ))
(test::assert-false (< 1.0 1.00000000000001 ))
<=
Usage: (<= val0 ... valN)
Less than or equal. Works for int, float or string.
Example:
(test::assert-true (<= 1 2))
(test::assert-true (<= 2 2))
(test::assert-true (<= 2 2 2))
(test::assert-true (<= 2 2 3))
(test::assert-true (<= 1.0 2.0))
(test::assert-true (<= 2.0 2.0))
(test::assert-true (<= 2.0 2.0 2.0))
(test::assert-true (<= 2.0 2.0 3.0))
(test::assert-false (<= 2.1 2.0 3.0))
(test::assert-false (<= 2 1))
(test::assert-false (<= 3 2 3))
(test::assert-true (<= 1.00000000000001 1.0000000000001 ))
(test::assert-true (<= 10.0000000000001 10.000000000001))
(test::assert-true (<= 100.000000000001 100.00000000001))
(test::assert-true (<= 1000.000000000001 1000.00000000001))
==
Usage: (== val0 ... valN)
Equals. Works for numeric types (int, float).
Example:
(test::assert-false (== 1 2))
(test::assert-true (== 2 2))
(test::assert-true (== 2 2 2))
(test::assert-false (== 3 2 2))
(test::assert-false (== 3.0 2.0))
(test::assert-true (== 2.0 2.0))
(test::assert-true (== 2.0 2.0 2.0))
(test::assert-false (== 3.0 2.0 2.0))
(test::assert-false (== 2.1 2.0 3.0))
(test::assert-false (== 2 1))
(test::assert-false (== 3 2 1))
(test::assert-false (== 1.1 1.0))
(test::assert-true (== 1.1 1.1))
(test::assert-false (== 3 2 3))
>
Usage: (> val0 ... valN)
Greater than. Works for int, float or string.
Example:
(test::assert-false (> 1 2))
(test::assert-false (> 2 2))
(test::assert-false (> 2 2 2))
(test::assert-false (> 3 2 2))
(test::assert-true (> 3.0 2.0))
(test::assert-false (> 2.0 2.0))
(test::assert-false (> 2.0 2.0 2.0))
(test::assert-false (> 3.0 2.0 2.0))
(test::assert-false (> 2.1 2.0 3.0))
(test::assert-true (> 2 1))
(test::assert-true (> 3 2 1))
(test::assert-true (> 1.1 1.0))
(test::assert-false (> 3 2 3))
(test::assert-true (> 1.001 1.0))
(test::assert-true (> 1.0000001 1.0))
(test::assert-false (> 1.00000000000001 1.0))
>=
Usage: (>= val0 ... valN)
Greater than or equal. Works for int, float or string.
Example:
(test::assert-false (>= 1 2))
(test::assert-true (>= 2 2))
(test::assert-true (>= 2 2 2))
(test::assert-true (>= 3 2 2))
(test::assert-true (>= 3.0 2.0))
(test::assert-true (>= 2.0 2.0))
(test::assert-true (>= 2.0 2.0 2.0))
(test::assert-true (>= 3.0 2.0 2.0))
(test::assert-false (>= 2.1 2.0 3.0))
(test::assert-true (>= 2 1))
(test::assert-true (>= 1.1 1.0))
(test::assert-false (>= 3 2 3))
(test::assert-true (>= 1.0000000000001 1.00000000000001))
(test::assert-true (>= 10.000000000001 10.0000000000001))
(test::assert-true (>= 100.00000000001 100.000000000001))
(test::assert-true (>= 1000.00000000001 1000.000000000001))
and
Usage: (and exp0 ... expN) -> [false(#f) or expN result]
Evaluates each form until one produces nil or false(#f), produces false(#f) if any form is nil/#f or the result of the last expression.
The and form will stop evaluating when the first expression produces nil/#f.
Example:
(test::assert-equal #f (and nil (err "and- can not happen")))
(test::assert-equal #f (and #f (err "and- can not happen")))
(test::assert-equal "and- done" (and #t "and- done"))
(test::assert-equal "and- done" (and #t #t "and- done"))
(test::assert-equal 6 (and #t #t (+ 1 2 3)))
(test::assert-equal 6 (and (/ 10 5) (* 5 2) (+ 1 2 3)))
cond
Usage: (cond ((test form*)*) -> result
Evaluate each test in order. If it is true then evaluate the form(s) in an implicit do and return the result. Stop evaluating at the first true test. Return nil if no conditions are true.
Example:
(def b 0)
(defn select-option (a)
(cond ((= a 1) "opt-one")
((= a 2) (set! b 5) "opt-two")
((= a 3) (str "opt" "-three"))))
(defn select-option-def (a)
(cond ((= a 1) "opt-one")
((= a 2) "opt-two")
((= a 3) (str "opt" "-three"))
(#t "default")))
(test::assert-equal "opt-one" (select-option 1))
(test::assert-equal b 0)
(test::assert-equal "opt-two" (select-option 2))
(test::assert-equal b 5)
(test::assert-equal "opt-three" (select-option 3))
(test::assert-equal nil (select-option 4))
(test::assert-equal "opt-one" (select-option-def 1))
(test::assert-equal "opt-two" (select-option-def 2))
(test::assert-equal "opt-three" (select-option-def 3))
(test::assert-equal "default" (select-option-def 4))
if
Usage: (if p1 a1 p2 a2 ... pn an?) -> [evaled form result]
If conditional. Will evaluate p1 and if true (i.e. not nil or false) then return the evaluation of a1, if falsey(i.e. nil or false) evaluate p2 and so on. On an odd number of arguments (an is missing) then evaluate and return pn. Return false(#f) if no predicate is true. This degenerates into the traditional (if predicate then-form else-form). NOTE: Both nil and false(#f) are 'falsey' for the purposes of if.
Example:
(def test-if-one
(if #t "ONE TRUE" "ONE FALSE"))
(def test-if-two
(if nil "TWO TRUE" "TWO FALSE"))
(def test-if-three
(if #f "THREE TRUE" "THREE FALSE"))
(test::assert-equal "ONE TRUE" test-if-one)
(test::assert-equal "TWO FALSE" test-if-two)
(test::assert-equal "THREE FALSE" test-if-three)
(def test-if-one2
(if #t "ONE2 TRUE"))
(def test-if-two2
(if nil "TWO2 TRUE"))
(def test-if-three2
(if #f "THREE2 TRUE"))
(test::assert-equal "ONE2 TRUE" test-if-one2)
(test::assert-equal #f test-if-two2)
(test::assert-equal #f test-if-three2)
(def test-if-one2
(if nil "ONE FALSE" #t "ONE TRUE" #t "ONE TRUE2"))
(def test-if-two2
(if nil "TWO TRUE" #f "TWO FALSE" #t "TWO TRUE2"))
(def test-if-three2
(if #f "THREE TRUE" nil "THREE FALSE" "THREE DEFAULT"))
(test::assert-equal "ONE TRUE" test-if-one2)
(test::assert-equal "TWO TRUE2" test-if-two2)
(test::assert-equal "THREE DEFAULT" test-if-three2)
(test::assert-equal nil (if nil))
(test::assert-equal #f (if nil #t nil #t nil #t))
match
Usage: (match condition (value form*)*) -> result
Evaluate condition and look for matching value in each branch of type (value form*). Form(s) will be wrapped in an implicit do. Use nil to take action if no match (encouraged!).
Example:
(defn select-option (a)
(match a (1 "opt-one")
(2 (set! b 5) "opt-two")
(3 (str "opt" "-three"))))
(defn select-option-def (a)
(match a (1 "opt-one")
(2 "opt-two")
(3 (str "opt" "-three"))
(nil "default")))
(def b 0)
(test::assert-equal b 0)
(test::assert-equal "opt-one" (select-option 1))
(test::assert-equal "opt-two" (select-option 2))
(test::assert-equal b 5)
(test::assert-equal "opt-three" (select-option 3))
(test::assert-equal #f (select-option 4))
(test::assert-equal "opt-one" (select-option-def 1))
(test::assert-equal "opt-two" (select-option-def 2))
(test::assert-equal "opt-three" (select-option-def 3))
(test::assert-equal "default" (select-option-def 4))
not
Usage: (not expression)
Return true(#t) if expression is nil, false(#f) otherwise.
Example:
(test::assert-true (not nil))
(test::assert-false (not 10))
(test::assert-false (not #t))
(test::assert-false (not (+ 1 2 3)))
or
Usage: (or exp0 ... expN) -> [false(#f) or first non nil expression]
Evaluates each form until one produces a non-nil/non-false result, produces #f if all expressions are 'falsey'.
The or form will stop evaluating when the first expression produces non-nil/false.
Example:
(test::assert-true (or nil nil #t (err "and- can not happen")))
(test::assert-true (or #f nil #t (err "and- can not happen")))
(test::assert-true (or #f #f #t (err "and- can not happen")))
(test::assert-equal #f (or nil nil nil))
(test::assert-equal #f (or #f nil nil))
(test::assert-equal #f (or #f nil #f))
(test::assert-equal #f (or #f #f #f))
(test::assert-equal "or- done" (or nil "or- done"))
(test::assert-equal "or- done" (or nil nil "or- done"))
(test::assert-equal 6 (or nil nil (+ 1 2 3)))
(test::assert-equal 2 (or (/ 10 5) (* 5 2) (+ 1 2 3)))
when
Usage: (when provided-condition if-true)
when is a convenience function used to check a form, provided-condition, and run some form, if-true, if provided-condition evaluates to true.
Example:
(test::assert-true (when #t #t))
(test::assert-false (when #t nil))
(test::assert-false (when nil nil))
conversion
List of symbols:
->key
Usage: (->key exp) -> keyword
Converts exp to a keyword.
No Examples
->sym
Usage: (->sym exp) -> symbol
Converts exp to a symbol.
No Examples
def?
Usage: (def? symbol) -> #t/#f
If symbol is defined then return true else false.
No Examples
ref
Usage: (ref symbol) -> Value
If symbol is defined then return the thing it references.
No Examples
core
List of symbols:
=, apply, back-quote, block, comp-time, dec!, def, defmacro, defn, do, doc, dotimes, dotimes-i, dyn, err, eval, fn, get-error, get-globals, get-in-namespace, get-namespaces, identity, import, inc!, len, let, let-while, load, loop, macro, mk-err, not=, not==, ns, nsubstitute!, occurs, on-raised-error, quote, recur, set!, substitute, to-list, to-vec, usage, with-ns
=
Usage: (= val0 val1)
Test equality, works for most value types where it makes sense, not just primitives.
Example:
(test::assert-false (= "aab" "aaa"))
(test::assert-true (= "aaa" "aaa"))
(test::assert-true (= "aaa" "aaa" "aaa"))
(test::assert-false (= "aaa" "aaaa" "aaa"))
(test::assert-false (= "ccc" "aab" "aaa"))
(test::assert-false (= "aaa" "aab"))
(test::assert-true (= (get-error (/ 1 0)) (get-error (/ 1 0))))
apply
Usage: (apply function arg* list)
Call the provided function with the supplied arguments, if last is a list or vector then it will be "spread" as arguments. For instance (apply pr 1 2 3 [4 5 6]) is equivalent to (pr 1 2 3 4 5 6).
Example:
(def test-apply-one (apply str "O" "NE"))
(test::assert-equal "ONE" test-apply-one)
(test::assert-equal 10 (apply + 1 2 7))
(test::assert-equal 10 (apply + 1 [2 7]))
(test::assert-equal 10 (apply + 1 '(2 7)))
(test::assert-equal 10 (apply + [1 2 7]))
(test::assert-equal 10 (apply + '(1 2 7)))
(def test-apply-fn1 (fn (& args) (apply + args)))
(test::assert-equal 10 (apply test-apply-fn1 1 2 7))
(test::assert-equal 10 (apply test-apply-fn1 1 [2 7]))
(test::assert-equal 10 (apply test-apply-fn1 1 '(2 7)))
(test::assert-equal 10 (apply test-apply-fn1 [1 2 7]))
(test::assert-equal 10 (apply test-apply-fn1 '(1 2 7)))
(def test-apply-fn2 (fn (x y z) (+ x y z)))
(test::assert-equal 10 (apply test-apply-fn2 1 2 7))
(test::assert-equal 10 (apply test-apply-fn2 1 [2 7]))
(test::assert-equal 10 (apply test-apply-fn2 1 '(2 7)))
(test::assert-equal 10 (apply test-apply-fn2 [1 2 7]))
(test::assert-equal 10 (apply test-apply-fn2 '(1 2 7)))
back-quote
Usage: `expression -> expression
Return expression without evaluation. Always use the ` reader macro or expansion will not work (i.e. (back-quote expression) will not do , expansion).
Backquote (unlike quote) allows for symbol/form evaluation using , or ,@.
Example:
(test::assert-equal (list 1 2 3) `(1 2 3))
(test::assert-equal `(1 2 3) '(1 2 3))
(def test-bquote-one 1)
(def test-bquote-list '(1 2 3))
(test::assert-equal (list 1 2 3) `(~test-bquote-one 2 3))
(test::assert-equal (list 1 2 3) `(~@test-bquote-list))
block
Usage: (get-error exp0 ... expN) -> pair
Evaluate each form (like do) but on error return (:error msg backtrace) instead of aborting. On success return (:ok . expN-result).
If there is no error will return the value of the last expression as the cdr of the pair. Always returns a pair with the first value either being :ok or :error.
Example:
(let (get-error-t1 (get-error (err (mk-err :string (str "Some Error")))))
(test::assert-equal :error (car get-error-t1))
(test::assert-equal "error [string]: \"Some Error\"" (str (cdr get-error-t1))))
(test::assert-equal "Some String" (get-error "Some String"))
(test::assert-equal "Some Other String" (get-error (let (test-get-error "Some ") (str test-get-error "Other String"))))
comp-time
Usage: (comp-time sexp+)
Compile and execute sexp+ at compile time. The result of the final sexp will then be compiled into the current module being compiled (produce nil to avoid this).
Example:
(with-ns test-out
(comp-time '(def ttf (fn () '(1 2 3))))
(comp-time (def ttf2 (fn () '(1 2 3))) nil)
(test::assert-equal '(1 2 3) (ttf))
(test::assert-equal '(1 2 3) (test-out::ttf))
(test::assert-equal '(1 2 3) (ttf2))
(test::assert-equal '(1 2 3) (test-out::ttf2)))
dec!
Usage: (dec! symbol [number]) -> new value
Decrement the value in symbol by one or the optional number
Example:
(def *dec-test* 5)
(test::assert-equal 4 (dec! *dec-test*))
(test::assert-equal 4 *dec-test*)
(test::assert-equal 1 (dec! *dec-test* 3))
(test::assert-equal 1 *dec-test*)
(let (dec-test 5)
(test::assert-equal 4 (dec! dec-test))
(test::assert-equal 4 dec-test)
(test::assert-equal 1 (dec! dec-test 3))
(test::assert-equal 1 dec-test))
def
Usage: (def symbol doc_string? expression) -> expression
Adds an expression to the current namespace. Return the expression that was defined. Symbol is not evaluated. Can take an option doc string (docstrings can only be set on namespaced (global) symbols).
Example:
(def test-do-one nil)
(def test-do-two nil)
(def test-do-three (do (set! test-do-one "One")(set! test-do-two "Two")"Three"))
(test::assert-equal "One" test-do-one)
(test::assert-equal "Two" test-do-two)
(test::assert-equal "Three" test-do-three)
(let (test-do-one nil)
; Add this to the let's scope (shadow the outer test-do-two).
(test::assert-equal "Default" (def test-do-four "Default"))
; set the currently scoped value.
(set! test-do-one "1111")
(set! test-do-two "2222")
(test::assert-equal "1111" test-do-one)
(test::assert-equal "2222" test-do-two)
(test::assert-equal "Default" test-do-four))
; Original outer scope not changed.
(test::assert-equal "One" test-do-one)
(test::assert-equal "Default" test-do-four)
defmacro
Usage: (defmacro name argument_list body)
Create a macro and bind it to a symbol in the current scope.
Example:
(defmacro test-mac (x) `(inc! ~x))
(def test-mac-x 2)
(test-mac test-mac-x)
(test::assert-equal 3 test-mac-x)
(defmacro test-mac (x) `(set! ~x 15))
(test-mac test-mac-x)
(test::assert-equal 15 test-mac-x)
defn
Usage: (defn name args body)
Define a named function in the current namespace.
Example:
(defn defn-test (x y) (+ x y))
(test::assert-equal 5 (defn-test 2 3))
(defn defn-test (x y) (set! x (* x 2)) (+ x y))
(test::assert-equal 7 (defn-test 2 3))
(defn defn-test (x y) nil)
(test::assert-false (defn-test 2 3))
(defn defn-test (x y) #t)
(test::assert-true (defn-test 2 3))
do
Usage: (do exp0 ... expN) -> expN
Evaluate each form and return the last.
Example:
(def test-do-one nil)
(def test-do-two nil)
(def test-do-three (do (set! test-do-one "One") (set! test-do-two "Two") "Three"))
(test::assert-equal "One" test-do-one)
(test::assert-equal "Two" test-do-two)
(test::assert-equal "Three" test-do-three)
doc
Usage: (doc sym [SCRATCH] [SCRATCH] docs has-usage)
Print the documentation for provided symbol.
No Examples
dotimes
Usage: (dotimes times body [SCRATCH] [SCRATCH] i-name)
Evaluate body a number of times equal to times' numerical value.
Example:
(def i 0)
(dotimes 11 (set! i (+ 1 i)))
(test::assert-equal 11 i)
dotimes-i
Usage: (dotimes-i idx-bind times body)
Evaluate body a number of times equal to times' numerical value. Includes an incrementing reference binding, idx-bind, accessible in body.
Example:
(def i 0)
(def i-tot 0)
(dotimes-i idx 11 (do (set! i-tot (+ idx i-tot))(set! i (+ 1 i))))
(test::assert-equal 11 i)
(test::assert-equal 55 i-tot)
dyn
Usage: (dyn key value expression) -> result_of_expression
Creates a dynamic binding for key, assigns value to it and evals expression under it. Note that if key must be a symbol and is not evaluated.
The binding is gone once the dyn form ends. This is basically a set! on the binding in an unwind protect to reset it when done. When used on a global will set the first binding found and reset it when done. Calls to dyn can be nested and previous dynamic values will be restored as interior dyn's exit.
Example:
(def *dyn-test* 1)
(defn test-dyn-fn (val) (str *dyn-test* val))
(def out (dyn *dyn-test* 11 (test-dyn-fn 101)))
(test::assert-equal "11101" (str out))
;; when file handling works
;;(defn test-dyn-fn () (prn "Print dyn out"))
;;(dyn *stdout* (open "/tmp/sl-sh.dyn.test" :create :truncate) (test-dyn-fn))
;;(test::assert-equal "Print dyn out" (read-line (open "/tmp/sl-sh.dyn.test" :read)))
err
Usage: (err :keyword value)
Raises an error with keyword and value. By default this will break into the debugger like a runtime error (use get-error to avoid this).
Example:
(let (error (get-error (err :test "Test error")))
(test::assert-equal :test (car error))
(test::assert-equal "Test error" (cdr error))
(test::assert-true (err? error)))
eval
Usage: (eval expression)
Evaluate the provided expression. If expression is a list it will be compiled and executed and the result returned other values will just be returned (i.e. (eval 1) = 1, (eval "test") = "test", (eval [1 2 3]) = [1 2 3], etc).
Note eval is a function not a special form, the provided expression will be evaluated as part of a call.
Example:
(test::assert-equal "ONE" (eval "ONE"))
(test::assert-equal 10 (eval 10))
(test::assert-equal [1 2 3] (eval [1 2 3]))
(test::assert-equal 10 (eval '(+ 1 2 7)))
(test::assert-equal 10 (eval '(apply + 1 2 7)))
(test::assert-equal 10 (eval '(apply + 1 '(2 7))))
(test::assert-equal 10 (eval '(apply + '(1 2 7))))
(test::assert-equal 10 (eval '(apply + 1 [2 7])))
(test::assert-equal 10 (eval '(apply + [1 2 7])))
fn
Usage: (fn (param*) expr*) -> exprN
Create a function (lambda).
Example:
(def test-fn1 nil)
(def test-fn2 nil)
(def test-fn3 nil)
(def test-fn-empty ((fn () nil)))
(test::assert-false test-fn-empty)
((fn () (set! test-fn1 1)))
(test::assert-equal 1 test-fn1)
((fn () (set! test-fn1 10)(set! test-fn2 2)))
(test::assert-equal 10 test-fn1)
(test::assert-equal 2 test-fn2)
((fn () (set! test-fn1 11)(set! test-fn2 20)(set! test-fn3 3)))
(test::assert-equal 11 test-fn1)
(test::assert-equal 20 test-fn2)
(test::assert-equal 3 test-fn3)
((fn (x y z) (set! test-fn1 x)(set! test-fn2 y)(set! test-fn3 z)) 12 21 30)
(test::assert-equal 12 test-fn1)
(test::assert-equal 21 test-fn2)
(test::assert-equal 30 test-fn3)
(test::assert-equal 63 ((fn (x y z) (set! test-fn1 x)(set! test-fn2 y)(set! test-fn3 z)(+ x y z)) 12 21 30))
get-error
Usage: (get-error exp0 ... expN) -> pair
Evaluate each form (like do) but on error return (:error msg backtrace) instead of aborting. On success return (:ok . expN-result).
If there is no error will return the value of the last expression as the cdr of the pair. Always returns a pair with the first value either being :ok or :error.
Example:
(let (get-error-t1 (get-error (err (mk-err :string (str "Some Error")))))
(test::assert-equal :error (car get-error-t1))
(test::assert-equal "error [string]: \"Some Error\"" (str (cdr get-error-t1))))
(test::assert-equal "Some String" (get-error "Some String"))
(test::assert-equal "Some Other String" (get-error (let (test-get-error "Some ") (str test-get-error "Other String"))))
get-globals
Usage: (get-globals)
Return a vector containing all the symbols currently defined globally.
No Examples
get-in-namespace
Usage: (get-in-namespace 'SYMBOL)
Return a vector containing all the globals currently defined namespace SYMBOL.
No Examples
get-namespaces
Usage: (get-namespaces)
Return a vector containing all the namespaces currently defined globally.
No Examples
identity
Usage: (identity arg)
Identity function.
Example:
(test::assert-equal 0 (identity 0))
import
Usage: (import namespace [:as symbol])
Will import a namespace. Without an :as then all symbols in the namespace will become available in the current namespace as if local. With [:as symbol] then all namespace symbols become available with symbol:: prepended.
Example:
(ns testing)
(def x #t)
(test::assert-true x)
(ns ::)
(test::assert-true testing::x)
(import testing)
(test::assert-true x)
(import testing :as t)
(test::assert-true t::x)
inc!
Usage: (inc! symbol [number]) -> new value
Increment the value in symbol by one or the optional number
Example:
(def *inc-test* 1)
(test::assert-equal 2 (inc! *inc-test*))
(test::assert-equal 2 *inc-test*)
(test::assert-equal 5 (inc! *inc-test* 3))
(test::assert-equal 5 *inc-test*)
(let (inc-test 1)
(test::assert-equal 2 (inc! inc-test))
(test::assert-equal 2 inc-test)
(test::assert-equal 5 (inc! inc-test 3))
(test::assert-equal 5 inc-test))
len
Usage: (len expression) -> int
Return length of supplied expression. The length of an atom is 1.
Example:
(test::assert-equal 0 (len nil))
(test::assert-equal 5 (len "12345"))
; Note the unicode symbol is only one char even though it is more then one byte.
(test::assert-equal 6 (len "12345Σ"))
(test::assert-equal 3 (len '(1 2 3)))
(test::assert-equal 3 (len [1 2 3]))
(test::assert-equal 3 (len (list 1 2 3)))
(test::assert-equal 3 (len (vec 1 2 3)))
(test::assert-equal 1 (len 100))
(test::assert-equal 1 (len 100.0))
(test::assert-equal 1 (len \tab))
let
Usage: (let vals &rest let-body)
Takes list, vals, of form ((binding0 sexp0) (binding1 sexp1) ...) and evaluates let-body with all values of binding bound to the result of the evaluation of sexp.
Example:
(def test-do-one "One1")
(def test-do-two "Two1")
(def test-do-three (let (test-do-one "One") (set! test-do-two "Two")(test::assert-equal "One" test-do-one)"Three"))
(test::assert-equal "One1" test-do-one)
(test::assert-equal "Two" test-do-two)
(test::assert-equal "Three" test-do-three)
((fn (idx) (let (v2 (+ idx 2) v3 (+ idx 3))
(test::assert-equal (+ idx 2) v2)
(test::assert-equal (+ idx 3) v3)
(if (< idx 5) (recur (+ idx 1)))))0)
((fn (idx) (let (v2 (+ idx 2) v3 (+ idx 3))
(test::assert-equal (+ idx 2) v2)
(test::assert-equal (+ idx 3) v3)
(if (< idx 5) (this-fn (+ idx 1)))))0)
let-while
Usage: (let-while (initial-bindings) (loop bindings) condition & let-body)
Takes list of initial bindings (done once before loop) of form (binding0 sexp0, binding1 sexp1, ...), and a list of loop bindings (done at the start of each iteration including the first) and evaluates let-body with all values of binding bound to the result of the evaluation of both bindings while condition is true.
Example:
; both of these examples create a vector and iterate to print all the elements
; use traditional lisp structure
(def test-res [])
(let-while (l [1 2 3]) (done (empty? l), f (first l), l (rest l)) (not done)
(prn f)
(vec-push! test-res f))
(let ([x y z] test-res)
(test::assert-equal 1 x)
(test::assert-equal 2 y)
(test::assert-equal 3 z))
; same thing using destructuring
(def test-res [])
(let-while (l [1 2 3]) (done (empty? l), [% f & l] l) (not done)
(prn f)
(vec-push! test-res f))
(let ([x y z] test-res)
(test::assert-equal 1 x)
(test::assert-equal 2 y)
(test::assert-equal 3 z))
load
Usage: (load path) -> [last form value]
Read and eval a file (from path- a string). The load special form executes at compile time. This means it's parameter must resolve at compile time. Most of the time you will want to use this in conjunction with 'with-ns' to namespace the contents. Note: on it's own does nothing with namespaces.
Example:
(comp-time (def test-temp-file (get-temp-file)) nil)
(defer (fs-rm test-temp-file))
(let (tst-file (fopen test-temp-file :create))
(defer (fclose tst-file))
(fprn tst-file "(with-ns test-load")
(fprn tst-file " (defn test-fn () '(1 2 3)))"))
(load test-temp-file) ; put stuff in it's own namespace
(test::assert-equal '(1 2 3) (test-load::test-fn))
(with-ns test-out2
(comp-time
(def test-temp-file (get-temp-file))
(let (tst-file (fopen test-temp-file :create))
(defer (fclose tst-file))
(fprn tst-file "(defn test-fn () '(1 2 3))"))
nil)
(defer (fs-rm test-temp-file))
(load test-temp-file) ; put new stuff in current namespace
(test::assert-equal '(1 2 3) (test-fn))
(test::assert-equal '(1 2 3) (test-out2::test-fn)))
loop
Usage: (loop params bindings body)
Binds bindings to parameters in body. Use recur with desired bindings for subsequent iteration. Within the loop the lambda 'break' will end the loop, break can take an option argument that is what the loop produces (nil if no argument).
Example:
(def tot 0)
(loop (idx) (3) (do
(set! tot (+ tot 1))
(if (> idx 1) (recur (- idx 1)))))
(test::assert-equal 3 tot)
(def tot 0)
(loop (idx) (0)
(set! tot (+ tot 1))
(when (not (= idx 2))
(recur (+ idx 1))))
(test::assert-equal 3 tot)
(test::assert-equal 11 (loop (idx) (0)
(if (= idx 2) (break 11))
(recur (+ idx 1))))
(test::assert-false (loop (idx) (0)
(if (= idx 2) (break nil))
(recur (+ idx 1))))
(test::assert-error (loop (idx) (0)
(if (= idx 2) (break 1 3))
(recur (+ idx 1))))
macro
Usage: (macro (args) `(apply + ,@args))
Define an anonymous macro.
Example:
(def test-macro1 nil)
(def test-macro2 nil)
(def test-macro-empty (macro () nil))
(test::assert-false (test-macro-empty))
(def test-mac nil)
(def mac-var 2)
(let (mac-var 3)
(set! test-mac (macro (x) (set! test-macro2 100) (test::assert-equal 3 mac-var) (* mac-var x))))
(set! test-macro1 (test-mac 10))
(test::assert-equal 30 test-macro1)
(test::assert-equal 100 test-macro2)
mk-err
Usage: (mk-err :keyword value)
Create an error object. This does not raise the error but merely creates it. Can use car/cdr to extract the keyword and value.
Example:
(let (error (mk-err :test "Test error"))
(test::assert-equal :test (car error))
(test::assert-equal "Test error" (cdr error))
(test::assert-true (err? error)))
not=
Usage: (not= arg1 arg2)
Test if two values are not equal using =
Example:
(test::assert-true (not= 0 1))
(test::assert-true (not= 1 1.0))
(test::assert-false (not= 2 2))
(test::assert-false (not= 0.0 -0.0))
not==
Usage: (not== arg1 arg2)
Test if two values are not numerically equal using ==
Example:
(test::assert-true (not== 0 1))
(test::assert-false (not== 1 1.0))
(test::assert-false (not== 0.0 -0.0))
(test::assert-false (not== 2 2))
ns
Usage: (ns SYMBOL)
Changes to namespace. This is "open-ended" change and is intended for use with the REPL prefer with-ns for scripts. The symbol "::" will return to the "root" namespace (i.e. no namespace prepended to globals). This will cause all globals defined to have namespace:: prepended. This will also clear any existing imports.
Example:
(ns testing)
(def x #t)
(test::assert-true x)
(ns ::)
(test::assert-true testing::x)
nsubstitute!
Usage: (nsubstitute! lst old-item new-item mods [SCRATCH] [SCRATCH] early-return)
Replaces all instances of old-item in lst with new-item. If last argument passed in is keyword :first only the first instance of old-item will be replaced.
Example:
(let (lst (list 1 2 3 4 5))
(test::assert-equal (list 1 2 2 4 5) (nsubstitute! lst 3 2))
(test::assert-equal (list 1 2 2 4 5) lst)
(test::assert-equal (list 1 3 2 4 5) (nsubstitute! lst 2 3 :first)))
occurs
Usage: (occurs (list 1 2 ...) 7)
Counts instances of item in sequence.
Example:
(test::assert-equal 1 (occurs (list 1 3 5 2 4 8 2 4 88 2 1) 8))
(test::assert-equal 3 (occurs (list 1 3 5 2 4 10 2 4 88 2 1) 2))
(test::assert-equal 0 (occurs (list 1 3 5 2 4 10 2 4 88 2 1) 42))
on-raised-error
Usage: (on-raised-error (fn (error) ...))
Low level (consider this unstable) interface to the raised error machinery. Useful for building higher level error handling (get-error for instance). It takes either Nil or a callable with one parameter. That parameter will be the error that was raised. The entire running "chunk" of code will be displaced for the installed handler. Probably best to use this with a continuation or a function that ends in a continuation call otherwise it may be difficult to reason about...
Will return the previously installed handler or Nil if one is not installed. Calling with Nil will return the old handler and clear it (no handler installed).
This special form will override breaking into the debugger when an error is raised.
Example:
(defmacro get-error-test (& body)
`(let (old-error (on-raised-error nil))
(defer (on-raised-error old-error))
(call/cc (fn (k) (on-raised-error (fn (err) (k (cons (car err)(cdr err)))))
(cons :ok (do ~@body))))))
(test::assert-equal (cons :ok 6) (get-error-test (let (x 1, y 5) (+ x y))))
(test::assert-equal '(:test . "error") (get-error-test (let (x 1, y 5) (err :test "error")(+ x y))))
quote
Usage: 'expression -> expression
Return expression without evaluation. The reader macro 'expression will expand to (quote expression).
Example:
(test::assert-equal (list 1 2 3) (quote (1 2 3)))
(test::assert-equal (list 1 2 3) '(1 2 3))
(test::assert-equal '(1 2 3) (quote (1 2 3)))
recur
Usage: (recur &rest)
Recursively call the enclosing function with the given parameters. Recur uses tail call optimization and must be in the tail position or it is an error. For a named function it would be equivalent to a normal recursive call in a tail position but it requires a tail position and does not need a name (a normal recursive call would work in a non-tail position but could blow the stack if it is to deep- unlike a recur or tail position recursive call). NOTE: potential footgun, the let macro expands to a lambda (fn) and a recur used inside the let would bind with the let not the enclosing lambda (this would apply to any macro that also expands to a lambda- this is by design with the loop macro but would be unexpected with let).
Example:
(def tot 0)
(loop (idx) (3) (do
(set! tot (+ tot 1))
(if (> idx 1) (recur (- idx 1)))))
(test::assert-equal 3 tot)
(set! tot 0)
((fn (idx) (do
(set! tot (+ tot 1))
(if (> idx 1) (recur (- idx 1)))))5)
(test::assert-equal 5 tot)
set!
Usage: (set! symbol expression) -> expression
Sets an existing expression in the current scope(s). Return the expression that was set. Symbol is not evaluated.
Set will set the first binding it finds starting in the current scope and then trying enclosing scopes until exhausted.
Example:
(def test-do-one nil)
(def test-do-two nil)
(def test-do-three (do (set! test-do-one "One")(set! test-do-two "Two")"Three"))
(test::assert-equal "One" test-do-one)
(test::assert-equal "Two" test-do-two)
(test::assert-equal "Three" test-do-three)
(let (test-do-one nil)
; set the currently scoped value.
(test::assert-equal "1111" (set! test-do-one "1111"))
(test::assert-equal "1111" test-do-one))
; Original outer scope not changed.
(test::assert-equal "One" test-do-one)
substitute
Usage: (substitute lst old-item new-item mods)
Replaces all instances of old-item in copy of lst with new-item. If last argument passed in is keyword :first only the first instance of old-item will be replaced.
Example:
(let (lst (list 1 2 3 4 3)
olst (list 1 2 3 4 3)
lst2 (list 1 2 3 3 3 4 5)
olst2 (list 1 2 3 3 3 4 5))
(test::assert-equal (list 1 2 10 4 10) (substitute lst 3 10))
(test::assert-equal (list 1 2 10 4 3) (substitute lst 3 10 :first))
(test::assert-equal olst lst)
(test::assert-equal (list 1 2 4 4 4 4 5) (substitute lst2 3 4))
(test::assert-equal (list 1 2 4 3 3 4 5) (substitute lst2 3 4 :first))
(test::assert-equal olst2 lst2))
to-list
Usage: (to-list any)
Turns any one value into a list. If that value or if it was a sequence a new sequence with the same values.
No Examples
to-vec
Usage: (to-list any)
Turns any one value into a vector. If that value or if it was a sequence a new sequence with the same values.
No Examples
usage
Usage: (usage 'symbol)
Provides usage information derived from the bytecode. Documentation can also have it's own usage string provided in the doc string but this function returns what the actual function's compiled code provides.
No Examples
with-ns
Usage: (with-ns SYMBOL sexp+)
Create a namespace and compile sexp+ within it. Restore the previous namespace when scope ends. THe symbol "::" will return to the "root" namespace (i.e. no namespace prepended to globals). This will cause all globals defined to have namespace:: prepended. This will also clear any existing imports.
Example:
(with-ns test-with-ns
(def ttf (fn () '(1 2 3)))
(test::assert-equal '(1 2 3) (ttf))
(test::assert-equal '(1 2 3) (test-out::ttf)))
(test::assert-equal '(1 2 3) (test-out::ttf))
doc
List of symbols:
build-doc, doc-map, get-exemptions, get-globals-sorted, legacy_forms
build-doc
Usage: (build-doc valid-filepath)
Uses mdbook to build the documentation for the given book.
Example:
#t
doc-map
Usage: (doc-map symbol)
Returns documentation for given symbol as map. Keyword is a documentation fragment (usage, section, description, example) and value is text describing given fragment.
Example:
#t
get-exemptions
Usage: (get-exemptions)
Return a vector containing all the symbols currently exempted from docs (so the build passes), Ideally this will be 0.
No Examples
get-globals-sorted
Usage: (get-globals-sorted)
Return a vector containing all the symbols currently defined globally in sorted order (alphanumerically).
No Examples
legacy_forms
Usage: (legacy_forms)
Gets list of all forms that were used in the previous version of sl_sh.
Example:
#t
file
List of symbols:
cd, fclose, fflush, fopen, fs-accessed, fs-base, fs-crawl, fs-dir?, fs-exists?, fs-file?, fs-len, fs-modified, fs-parent, fs-rm, fs-same?, get-temp, get-temp-file, glob, read, read-all, read-line, temp-dir, with-temp, with-temp-file
cd
Usage: (cd dir-to-change-to)
Change directory.
Example:
(with-temp (fn (tmp)
(fclose (fopen (str tmp "/fs-cd-marker") :create :truncate))
(test::assert-false (fs-exists? "fs-cd-marker"))
(cd tmp)
(test::assert-true (fs-exists? "fs-cd-marker"))
(cd)))
fclose
Usage: (fclose file)
Close a file.
Example:
(with-temp-file (fn (tmp-file)
(let (tst-file (fopen tmp-file :create :truncate))
(fprn tst-file "Test Line Two")
(fclose tst-file)
(set! tst-file (fopen tmp-file :read))
(defer (fclose tst-file))
(test::assert-equal "Test Line Two
" (read-line tst-file)))))
fflush
Usage: (flush file)
Flush a file.
Example:
(with-temp-file (fn (tmp-file)
(let (tst-file (fopen tmp-file :create :truncate)
tst-file-read (fopen tmp-file :read))
(defer (fclose tst-file))
(defer (fclose tst-file-read))
(fprn tst-file "Test Line Three")
(fflush tst-file)
(test::assert-equal "Test Line Three
" (read-line tst-file-read)))))
fopen
Usage: (fopen filename option*)
Open a file. If you use :read and :write then you get a read/write unbuffered file. Including one of :read or :write will provide a file buffered for read or write (this is faster). Note: :append, :truncate, :create, :create-new all imply :write.
Options are: :read :write :append :truncate :create :create-new :on-error-nil
Example:
(with-temp-file (fn (tmp-file)
(let (test-open-f (fopen tmp-file :create :truncate))
(fprn test-open-f "Test Line One")
(fclose test-open-f)
(set! test-open-f (fopen tmp-file :read))
(defer (fclose test-open-f))
(test::assert-equal "Test Line One
" (read-line test-open-f)))))
fs-accessed
Usage: (fs-accessed /path/to/file/or/dir)
Returns the unix time file last accessed in ms.
Example:
(with-temp-file (fn (tmp)
(let (tst-file (fopen tmp :create)
last-acc (fs-accessed tmp))
(fclose tst-file)
(let (tst-file (fopen tmp :read))
(test::assert-true (>= (fs-accessed tmp) last-acc))
(fclose tst-file)))))
fs-base
Usage: (fs-base /path/to/file/or/dir)
Returns base name of file or directory passed to function.
No Examples
fs-crawl
Usage: (fs-crawl /path/to/file/or/dir (fn (x) (prn "found path" x) [max-depth] [:follow-syms])
If a directory is provided the path is recursively searched and every file and directory is called as an argument to the provided function. If a file is provided the path is provided as an argument to the provided function. Takes two optional arguments (in any order) an integer, representing max depth to traverse if file is a directory, or the symbol, :follow-syms, to follow symbol links when traversing if desired.
Example:
(with-temp-file (fn (tmp-file)
(let (cnt 0)
(fs-crawl tmp-file (fn (x)
(test::assert-equal (fs-base tmp-file) (fs-base x))
(set! cnt (+ 1 cnt))))
(test::assert-equal 1 cnt))))
(defn create-in (in-dir num-files visited)
(dotimes-i i num-files
(let (tmp-file (get-temp-file in-dir))
(set! visited.~tmp-file #f))))
(defn create-dir (tmp-dir visited)
(let (new-tmp (get-temp tmp-dir))
(set! visited.~new-tmp #f)
new-tmp))
(with-temp (fn (root-tmp-dir)
(let (tmp-file-count 5
visited {}
cnt 0)
(set! visited.~root-tmp-dir #f)
(create-in root-tmp-dir tmp-file-count visited)
(let (tmp-dir (create-dir root-tmp-dir visited)
new-files (create-in tmp-dir tmp-file-count visited)
tmp-dir (create-dir tmp-dir visited)
new-files (create-in tmp-dir tmp-file-count visited))
(fs-crawl root-tmp-dir (fn (x)
(let (file visited.~x)
(test::assert-true (not file)) ;; also tests double counting
(set! visited.~x #t)
(inc! cnt))))
(test::assert-equal (+ 3 (* 3 tmp-file-count)) cnt)
(test::assert-equal (+ 3 (* 3 tmp-file-count)) (len visited))
(seq-for key in (hash-keys visited) (test::assert-true visited.~key))))))
(with-temp (fn (root-tmp-dir)
(let (tmp-file-count 5
visited {}
cnt 0)
(set! visited.~root-tmp-dir #f)
(create-in root-tmp-dir tmp-file-count visited)
(let (tmp-dir (create-dir root-tmp-dir visited)
new-files (create-in tmp-dir tmp-file-count visited)
tmp-dir (create-dir tmp-dir {})
new-files (do (set! visited.~tmp-dir #f)(create-in tmp-dir tmp-file-count {})))
(fs-crawl root-tmp-dir (fn (x)
(let (file visited.~x)
(test::assert-true (not file)) ;; also tests double counting
(set! visited.~x #t)
(inc! cnt))) 2)
(test::assert-equal (+ 3 (* 2 tmp-file-count)) cnt)
(test::assert-equal (+ 3 (* 2 tmp-file-count)) (len visited))
(seq-for key in (hash-keys visited) (test::assert-true visited.~key))))))
(with-temp (fn (root-tmp-dir)
(let (tmp-file-count 5
visited {}
cnt 0)
(set! visited.~root-tmp-dir #f)
(create-in root-tmp-dir tmp-file-count visited)
(let (tmp-dir (create-dir root-tmp-dir {})
new-files (do (set! visited.~tmp-dir #f)(create-in tmp-dir tmp-file-count {}))
tmp-dir (create-dir tmp-dir {})
new-files (create-in tmp-dir tmp-file-count {}))
(fs-crawl root-tmp-dir (fn (x)
(let (file visited.~x)
(test::assert-true (not file)) ;; also tests double counting
(set! visited.~x #t)
(inc! cnt))) 1)
(test::assert-equal (+ 2 tmp-file-count) cnt)
(test::assert-equal (+ 2 tmp-file-count) (len visited))
(seq-for key in (hash-keys visited) (test::assert-true visited.~key))))))
fs-dir?
Usage: (fs-dir? path-to-test)
Is the given path a directory?
Example:
(with-temp (fn (tmp)
(fclose (fopen (str tmp "/fs-dir-file") :create :truncate))
(test::assert-false (fs-dir? (str tmp "/fs-dir-file")))
(test::assert-true (fs-dir? tmp))
(test::assert-false (fs-file? (str tmp "/fs-dir-nope")))))
fs-exists?
Usage: (fs-exists? path-to-test)
Does the given path exist?
Example:
(with-temp (fn (tmp)
(fclose (fopen (str tmp "/fs-exists") :create :truncate))
(test::assert-true (fs-exists? (str tmp "/fs-exists")))
(test::assert-true (fs-exists? tmp))
(test::assert-false (fs-exists? (str tmp "/fs-exists-nope")))))
fs-file?
Usage: (fs-file? path-to-test)
Is the given path a file?
Example:
(with-temp (fn (tmp)
(fclose (fopen (str tmp "/fs-file") :create :truncate))
(test::assert-true (fs-file? (str tmp "/fs-file")))
(test::assert-false (fs-file? tmp))
(test::assert-false (fs-file? (str tmp "/fs-file-nope")))))
fs-len
Usage: (fs-len /path/to/file/or/dir)
Returns the size of the file in bytes.
Example:
(with-temp-file (fn (tmp)
(let (tst-file (fopen tmp :create :truncate))
(fprn tst-file "Test Line Read Line One")
(fpr tst-file "Test Line Read Line Two")
(fclose tst-file)
(test::assert-equal 47 (fs-len tmp)))))
fs-modified
Usage: (fs-modified /path/to/file/or/dir)
Returns the unix time file last modified in ms.
Example:
(with-temp-file (fn (tmp)
(let (tst-file (fopen tmp :create :truncate)
last-mod (fs-modified tmp))
(fprn tst-file "Test Line Read Line One")
(fpr tst-file "Test Line Read Line Two")
(fflush tst-file)
(fclose tst-file)
(test::assert-true (>= (fs-modified tmp) last-mod)))))
fs-parent
Usage: (fs-parent /path/to/file/or/dir)
Returns base name of file or directory passed to function.
No Examples
fs-rm
Usage: (fs-rm "/dir/or/file/to/remove")
Takes a file or directory as a string and removes it. Works recursively for directories.
Example:
(def fp nil)
(let (a-file (get-temp-file))
(test::assert-true (fs-exists? a-file))
(set! fp a-file)
(fs-rm a-file))
(test::assert-false (nil? fp))
(test::assert-false (fs-exists? fp))
fs-same?
Usage: (fs-same? /path/to/file/or/dir /path/to/file/or/dir)
Returns true if the two provided file paths refer to the same file or directory.
Example:
(with-temp-file (fn (tmp-file)
(test::assert-true (fs-same? tmp-file tmp-file))))
get-temp
Usage: (get-temp ["/path/to/directory/to/use/as/base" "optional-prefix" "optional-suffix" length])
Creates a directory inside of an OS specific temporary directory. See temp-dir for OS specific notes. Also accepts an optional prefix, an optional suffix, and an optional length for the random number of characters in the temporary file created. Defaults to prefix of ".tmp", no suffix, and five random characters.
Example:
(test::assert-true (str-contains (get-temp) (temp-dir)))
(with-temp (fn (tmp)
(let (tmp-dir (get-temp tmp))
(test::assert-true (str-contains tmp-dir tmp)))))
(with-temp (fn (tmp)
(let (tmp-dir (get-temp tmp "some-prefix"))
(test::assert-true (str-contains tmp-dir tmp))
(test::assert-true (str-contains tmp-dir "some-prefix")))))
(with-temp (fn (tmp)
(let (tmp-dir (get-temp tmp "some-prefix" "some-suffix"))
(test::assert-true (str-contains tmp-dir tmp))
(test::assert-true (str-contains tmp-dir "some-prefix"))
(test::assert-true (str-contains tmp-dir "some-suffix")))))
(with-temp (fn (tmp)
(let (tmp-dir (get-temp tmp "some-prefix" "some-suffix" 6))
(test::assert-true (str-contains tmp-dir tmp))
(test::assert-true (str-contains tmp-dir "some-prefix"))
(test::assert-true (str-contains tmp-dir "some-suffix"))
(test::assert-equal (len "some-prefix012345some-suffix") (len (fs-base tmp-dir))))))
get-temp-file
Usage: (get-temp-file ["/path/to/directory/to/use/as/base" "optional-prefix" "optional-suffix" length])
Returns name of file created inside temporary directory. Optionally takes a directory to use as the parent directory of the temporary file. Also accepts an optional prefix, an optional suffix, and an optional length for the random number of characters in the temporary files created. Defaults to prefix of ".tmp", no suffix, and five random characters.
Example:
(test::assert-true (str-contains (get-temp-file) (temp-dir)))
(with-temp (fn (tmp)
(let (tmp-file (get-temp-file tmp))
(test::assert-true (str-contains tmp-file tmp)))))
(with-temp (fn (tmp)
(let (tmp-file (get-temp-file tmp "some-prefix"))
(test::assert-true (str-contains tmp-file "some-prefix")))))
(with-temp (fn (tmp)
(let (tmp-file (get-temp-file tmp "some-prefix" "some-suffix"))
(test::assert-true (str-contains tmp-file "some-prefix"))
(test::assert-true (str-contains tmp-file "some-suffix")))))
(with-temp (fn (tmp)
(let (tmp-file (get-temp-file tmp "some-prefix" "some-suffix" 10))
(test::assert-true (str-contains tmp-file "some-prefix"))
(test::assert-true (str-contains tmp-file "some-suffix"))
(test::assert-equal (len "some-prefix0123456789some-suffix") (len (fs-base tmp-file))))))
glob
Usage: (glob /path/with/*)
Takes a list/varargs of globs and return the list of them expanded.
Example:
(with-temp (fn (tmp)
(fclose (fopen (str tmp "/g1") :create :truncate))
(fclose (fopen (str tmp "/g2") :create :truncate))
(fclose (fopen (str tmp "/g3") :create :truncate))
(test::assert-equal [(str tmp "/g1") (str tmp "/g2") (str tmp "/g3")] (glob (str tmp "/*")))))
read
Usage: (read file|string end-exp?) -> expression
Read a file or string and return the next object (symbol, string, list, etc). Raises an error if the file or string has been read unless end-exp is provided then returns that on the end condition. Note: When reading a string read always starts at the beginning of the string.
Example:
(with-temp-file (fn (tmp)
(let (tst-file (fopen tmp :create :truncate)
test-str nil)
(fprn tst-file "(1 2 3)(x y z)")
(fclose tst-file)
(set! tst-file (fopen tmp :read))
(test::assert-equal '(1 2 3) (read tst-file))
(test::assert-equal '(x y z) (read tst-file))
(test::assert-error (read tst-file))
(fclose tst-file)
(set! tst-file (fopen tmp :read))
(test::assert-equal '(1 2 3) (read tst-file :done))
(test::assert-equal '(x y z) (read tst-file :done))
(test::assert-equal :done (read tst-file :done))
(fclose tst-file)
(test::assert-equal '(4 5 6) (read "(4 5 6)"))
(set! test-str "7 8 9")
(test::assert-equal 7 (read test-str))
(test::assert-equal 7 (read test-str))
(test::assert-equal '(x y z) (read "(x y z)")))))
read-all
Usage: (read-all file|string) -> vec
Read a file or string and return the vector of contained expressions. This reads the entire file or string and will wrap it in an outer vector (always returns a vector).
Unlike most lisp readers this one will put loose symbols in a vector (i.e. you enter things at the repl without the enclosing parens).
If the read item is empty (including a comment) then will return an empty vector.
Example:
(with-temp-file (fn (tmp)
(let (tst-file (fopen tmp :create :truncate)
test-str nil)
(fprn tst-file "(1 2 3)(x y z)")
(fclose tst-file)
(set! tst-file (fopen tmp :read))
(test::assert-equal ['(1 2 3)'(x y z)] (read-all tst-file))
(fclose tst-file)
(test::assert-equal ['(4 5 6)] (read-all "(4 5 6)"))
(test::assert-equal [7 8 9] (read-all "7 8 9"))
(test::assert-equal ['(x y z)] (read-all "(x y z)"))
(test::assert-equal [] (read-all ";(x y z)")))))
read-line
Usage: (read-line file) -> string
Read a line from a file. Returns Nil if there is nothing left to read.
Example:
(with-temp-file (fn (tmp)
(let (tst-file (fopen tmp :create :truncate))
(fprn tst-file "Test Line Read Line One")
(fpr tst-file "Test Line Read Line Two")
(fclose tst-file)
(set! tst-file (fopen tmp :read))
(defer (fclose tst-file))
(test::assert-equal "Test Line Read Line One\n" (read-line tst-file))
(test::assert-equal "Test Line Read Line Two" (read-line tst-file)))))
temp-dir
Usage: (temp-dir)
Returns a string representing the temporary directory. See get-temp for higher level temporary directory creation mechanism.
On Unix: Returns the value of the TMPDIR environment variable if it is set, otherwise for non-Android it returns /tmp. If Android, since there is no global temporary folder (it is usually allocated per-app), it returns /data/local/tmp.
On Windows: Returns the value of, in order, the TMP, TEMP, USERPROFILE environment variable if any are set and not the empty string. Otherwise, temp_dir returns the path of the Windows directory. This behavior is identical to that of GetTempPath, which this function uses internally.
Example:
(test::assert-true (fs-dir? (temp-dir)))
with-temp
Usage: (with-temp (fn (x) (println "given temp dir:" x)) ["optional-prefix" "optional-suffix" length])
Takes a function that accepts a temporary directory. This directory will be recursively removed when the provided function is finished executing. Also accepts an optional prefix, an optional suffix, and an optional length for the random number of characters in the temporary directory created. Defaults to prefix of ".tmp", no suffix, and five random characters.
Example:
(def fp nil)
(with-temp (fn (tmp-dir)
(let (tmp-file (str tmp-dir "/sl-sh-tmp-file.txt")
a-file (fopen tmp-file :create :truncate))
(test::assert-true (fs-exists? tmp-file))
(set! fp tmp-file)
(fclose a-file))))
(test::assert-false (nil? fp))
(test::assert-false (fs-exists? fp))
(with-temp
(fn (tmp)
(test::assert-true (str-contains tmp "some-prefix")))
"some-prefix")
(with-temp
(fn (tmp)
(test::assert-true (str-contains tmp "some-prefix"))
(test::assert-true (str-contains tmp "some-suffix")))
"some-prefix"
"some-suffix")
(with-temp
(fn (tmp)
(test::assert-true (str-contains tmp "some-prefix"))
(test::assert-true (str-contains tmp "some-suffix"))
(test::assert-equal (len "some-prefix0123456789some-suffix") (len (fs-base tmp))))
"some-prefix"
"some-suffix"
10)
with-temp-file
Usage: (with-temp-file (fn (x) (println "given temp file:" x)) ["optional-prefix" "optional-suffix" length])
Takes a function that accepts a temporary file. This file will be removed when the provided function is finished executing. Also accepts an optional prefix, an optional suffix, and an optional length for the random number of characters in the temporary file created. Defaults to prefix of ".tmp", no suffix, and five random characters.
Example:
(def fp nil)
(with-temp-file (fn (tmp-file)
(let (a-file (fopen tmp-file :create :truncate))
(test::assert-true (fs-exists? tmp-file))
(set! fp tmp-file)
(fclose a-file))))
(test::assert-false (nil? fp))
(test::assert-false (fs-exists? fp))
(with-temp-file
(fn (tmp)
(test::assert-true (str-contains tmp "some-prefix")))
"some-prefix")
(with-temp-file
(fn (tmp)
(test::assert-true (str-contains tmp "some-prefix"))
(test::assert-true (str-contains tmp "some-suffix")))
"some-prefix"
"some-suffix")
(with-temp-file
(fn (tmp)
(test::assert-true (str-contains tmp "some-prefix"))
(test::assert-true (str-contains tmp "some-suffix"))
(test::assert-equal (len "some-prefix0123456789some-suffix") (len (fs-base tmp))))
"some-prefix"
"some-suffix"
10)
hashmap
{{ #include section-docs/hashmap.md }}
List of symbols:
hash-keys, hash-remove!, make-hash
hash-keys
Usage: (hash-keys hashmap)
Returns a vector of all the hashmaps keys. The keys will be unordered.
Example:
(def tst-hash {:key1 "val one" 'key2 "val two" "key3" "val three" \S "val S"})
(test::assert-equal 4 (len (hash-keys tst-hash)))
(test::assert-true (in? (hash-keys tst-hash) :key1) " Test :key1")
(test::assert-true (in? (hash-keys tst-hash) 'key2) " Test key2")
(test::assert-true (in? (hash-keys tst-hash) \S) " Test S")
(test::assert-true (in? (hash-keys tst-hash) "key3") " Test key3")
(test::assert-false (in? (hash-keys tst-hash) :key4))
hash-remove!
Usage: (hash-remove! hashmap key)
Remove a key from a hashmap. This is a destructive form!
Example:
(def tst-hash {:key1 "val one" 'key2 "val two" "key3" "val three" \S "val S"})
(test::assert-equal 4 (len (hash-keys tst-hash)))
(test::assert-equal "val one" tst-hash.:key1)
(test::assert-equal "val two" (get tst-hash 'key2))
(test::assert-equal "val three" (get tst-hash "key3"))
(test::assert-equal "val S" (get tst-hash \S))
(hash-remove! tst-hash 'key2)
(test::assert-equal 3 (len (hash-keys tst-hash)))
(test::assert-equal "val one" tst-hash.:key1)
(test::assert-equal "val three" (get tst-hash "key3"))
(test::assert-equal "val S" (get tst-hash \S))
(hash-remove! tst-hash :key1)
(test::assert-equal 2 (len (hash-keys tst-hash)))
(test::assert-equal "val three" (get tst-hash "key3"))
(test::assert-equal "val S" (get tst-hash \S))
(hash-remove! tst-hash "key3")
(test::assert-equal 1 (len (hash-keys tst-hash)))
(test::assert-equal "val S" (get tst-hash \S))
(hash-remove! tst-hash \S)
(test::assert-equal 0 (len (hash-keys tst-hash)))
make-hash
Usage: (make-hash associations?)
Make a new hash map.
If associations is provided (makes an empty map if not) then it is a list of pairs (key . value) that populate the initial map. Neither key nor value in the associations will be evaluated.
No Examples
io
List of symbols:
fs-meta
Usage: (fs-meta [FILENAME]) -> map
Returns a map of a files meta data.
No Examples
iterator
List of symbols:
iter::enumerate, iter::file-iter, iter::filter, iter::for, iter::iter, iter::iter-or-single, iter::iter?, iter::list-iter, iter::map, iter::mk-iter, iter::once-iter, iter::range, iter::reduce, iter::repeat-iter, iter::string-iter, iter::vec-iter, iter::vec-iter-pair, iter::vec-iter-rev
iter::enumerate
Usage: (iter::enumerate iter start [SCRATCH] [SCRATCH] idx [SCRATCH] [SCRATCH] iter)
Iterator that wraps an iterator and generates pairs of current index and value. Index is 0 based by default, takes an optional second parameter with the start index.
Example:
(import iter)
(let (test-iter (enumerate (vec-iter [:a :b :c])))
(let ([i v] (test-iter)) (test::assert-equal 0 i) (test::assert-equal :a v))
(let ([i v] (test-iter)) (test::assert-equal 1 i) (test::assert-equal :b v))
(let ([i v] (test-iter)) (test::assert-equal 2 i) (test::assert-equal :c v))
(test::assert-equal :*iter-empty* (test-iter)))
(let (test-iter (enumerate (vec-iter [:a :b :c]) 5))
(let ([i v] (test-iter)) (test::assert-equal 5 i) (test::assert-equal :a v))
(let ([i v] (test-iter)) (test::assert-equal 6 i) (test::assert-equal :b v))
(let ([i v] (test-iter)) (test::assert-equal 7 i) (test::assert-equal :c v))
(test::assert-equal :*iter-empty* (test-iter)))
iter::file-iter
Usage: (iter::file-iter f [SCRATCH] [SCRATCH] iter)
Iterator that wraps a file. Each call produces the next line (with trailing newline).
Example:
(import iter)
(with-temp-file (fn (file-name)
(let (tst-file (fopen file-name :create :truncate))
(defer (fclose tst-file))
(fprn tst-file "line 1")
(fprn tst-file "line 2")
(fprn tst-file "line 3")
(fpr tst-file "line 4"))
(let (tst-file (fopen file-name), test-iter (file-iter tst-file))
(defer (fclose tst-file))
(test::assert-equal "line 1\n" (test-iter))
(test::assert-equal "line 2\n" (test-iter))
(test::assert-equal "line 3\n" (test-iter))
(test::assert-equal "line 4" (test-iter))
(test::assert-equal :*iter-empty* (test-iter)))))
iter::filter
Usage: (iter::filter iter lambda [SCRATCH] [SCRATCH] iter)
Returns a filter-iter around iter. Iterator that applies a lambda to each element to determine if is returned- is lazy.
Example:
(let (test-iter (iter::filter (iter::vec-iter [1 2 3]) (fn (x) (not (= x 2)))))
(test::assert-equal 1 (test-iter))
(test::assert-equal 3 (test-iter))
(test::assert-equal :*iter-empty* (test-iter)))
iter::for
Usage: (iter::for bind in items body [SCRATCH] [SCRATCH] i-name actual-bind)
Loops over each element in an iterator. The bind parameter is bound to the current element of items and is accessible in body. Body is evaluated a number of times equal to the items in the iterator.
Example:
(import iter)
(let (i 0)
(for x in (range 0 11) (set! i (+ 1 i)))
(test::assert-equal 11 i))
(let (v [:a :b :c], iter (enumerate (vec-iter v)))
(for [idx, val] in iter (test::assert-equal v.~idx val)))
iter::iter
Usage: (iter::iter thing)
Return thing as an iterator if possible (if it is an iterator just return thing).
Example:
(import iter)
(test::assert-true (iter? (iter '(1 2 3))))
(test::assert-true (iter? (iter [1 2 3])))
(test::assert-true (iter? (iter "abc")))
(test::assert-true (iter? (iter (iter '(1 2 3)))))
iter::iter-or-single
Usage: (iter::iter-or-single thing)
Return thing as an iterator if possible (if it is an iterator just return thing). If not possible then wrap thing in a once iter and return that.
Example:
(import iter)
(test::assert-true (iter? (iter-or-single '(1 2 3))))
(test::assert-true (iter? (iter-or-single [1 2 3])))
(test::assert-true (iter? (iter-or-single "abc")))
(test::assert-true (iter? (iter-or-single (iter '(1 2 3)))))
(test::assert-true (iter? (iter-or-single 1)))
(test::assert-true (iter? (iter-or-single \A)))
iter::iter?
Usage: (iter::iter? test)
Return true if thing is an iterator, false otherwise.
Example:
(import iter)
(test::assert-true (iter? (list-iter '(1 2 3))))
(test::assert-false (iter? '(1 2 3)))
iter::list-iter
Usage: (iter::list-iter l [SCRATCH] [SCRATCH] iter)
Iterator that wraps a list.
Example:
(import iter)
(let (test-list-iter (list-iter '(1 2 3)))
(test::assert-equal 1 (test-list-iter))
(test::assert-equal 2 (test-list-iter))
(test::assert-equal 3 (test-list-iter))
(test::assert-equal :*iter-empty* (test-list-iter))
(set! test-list-iter (list-iter '()))
(test::assert-equal :*iter-empty* (test-list-iter)))
iter::map
Usage: (iter::map i lambda [SCRATCH] [SCRATCH] iter)
Iterator that applies a lambda to each element of another iterator- is lazy.
Example:
(import iter)
(let (test-map-iter (map (list-iter '(1 2 3)) (fn (x) (* x 2))))
(test::assert-equal 2 (test-map-iter))
(test::assert-equal 4 (test-map-iter))
(test::assert-equal 6 (test-map-iter))
(test::assert-equal :*iter-empty* (test-map-iter)))
iter::mk-iter
Usage: (iter::mk-iter body)
Helper to create an iterator. Will make sure it has the correct property set.
Example:
iter::once-iter
Usage: (iter::once-iter i [SCRATCH] [SCRATCH] iter)
Iterator that wraps and returns a single object once.
Example:
(import iter)
(let (test-iter (once-iter 3))
(test::assert-equal 3 (test-iter))
(test::assert-equal :*iter-empty* (test-iter))
(set! test-iter (once-iter "iter"))
(test::assert-equal "iter" (test-iter))
(test::assert-equal :*iter-empty* (test-iter)))
iter::range
Usage: (iter::range start end [SCRATCH] [SCRATCH] iter)
Iterator that generates numbers within a range.
Example:
(import iter)
(let (test-iter (range 3 6))
(test::assert-equal 3 (test-iter))
(test::assert-equal 4 (test-iter))
(test::assert-equal 5 (test-iter))
(test::assert-equal :*iter-empty* (test-iter)))
iter::reduce
Usage: (iter::reduce iter acc reducing-fn [SCRATCH] [SCRATCH] #SYM:367:2 #SYM:367:3 #SYM:367:3 [SCRATCH] val)
reduce is used to amalgamate an iterator and an initial value, according to the reducing function provided. The reducing-fcn should be a function of two arguments. In the first iteration of reduce, the init-val will be used as the first argument to the reducing-fcn and (iter) will be used as the second argument. For all subsequent iterations, The result from the previous application of the reducing-fcn will be used as the first argument to the reducing-fcn and the second argument will be the next item in the collection when the collection is empty reduce will return the amalgamated result.
Example:
(import iter)
(test::assert-true (= 15 (iter::reduce (iter::vec-iter [1 2 3 4 5]) 0 +)))
(test::assert-true (= 16 (iter::reduce (iter::vec-iter [1 2 3 4 5]) 1 +)))
(test::assert-true (= "one hoopy frood" (iter::reduce (iter::vec-iter ["one " "hoopy " "frood"]) "" str)))
iter::repeat-iter
Usage: (iter::repeat-iter i [SCRATCH] [SCRATCH] iter)
Iterator that wraps and returns a single object on each call.
Example:
(import iter)
(let (test-iter (repeat-iter 3))
(test::assert-equal 3 (test-iter))
(test::assert-equal 3 (test-iter))
(test::assert-equal 3 (test-iter))
(test::assert-equal 3 (test-iter))
(test::assert-equal 3 (test-iter))
(set! test-iter (repeat-iter "iter"))
(test::assert-equal "iter" (test-iter))
(test::assert-equal "iter" (test-iter))
(test::assert-equal "iter" (test-iter))
(test::assert-equal "iter" (test-iter))
(test::assert-equal "iter" (test-iter)))
iter::string-iter
Usage: (iter::string-iter s [SCRATCH] [SCRATCH] idx slen [SCRATCH] [SCRATCH] iter)
Iterator that wraps a string. Each element is the next character.
Example:
(import iter)
(let (test-string-iter (string-iter "123"))
(test::assert-equal \1 (test-string-iter))
(test::assert-equal \2 (test-string-iter))
(test::assert-equal \3 (test-string-iter))
(test::assert-equal :*iter-empty* (test-string-iter)))
iter::vec-iter
Usage: (iter::vec-iter v [SCRATCH] [SCRATCH] idx vlen [SCRATCH] [SCRATCH] iter)
Iterator that wraps a vector.
Example:
(import iter)
(let (test-vec-iter (vec-iter [1 2 3]))
(test::assert-equal 1 (test-vec-iter))
(test::assert-equal 2 (test-vec-iter))
(test::assert-equal 3 (test-vec-iter))
(test::assert-equal :*iter-empty* (test-vec-iter))
(set! test-vec-iter (vec-iter (vec)))
(test::assert-equal :*iter-empty* (test-vec-iter)))
iter::vec-iter-pair
Usage: (iter::vec-iter-pair v [SCRATCH] [SCRATCH] fidx bidx [SCRATCH] [SCRATCH] iter iter)
Return a pair of iterators, one forward and one reverse for a vector. The two iterators will not overlap (i.e. the forward and reverse will never produce the same items).
Example:
(import iter)
(let ([fwd-iter, rev-iter] (vec-iter-pair [1 2 3]))
(test::assert-equal 1 (fwd-iter))
(test::assert-equal 3 (rev-iter))
(test::assert-equal 2 (fwd-iter))
(test::assert-equal :*iter-empty* (rev-iter))
(test::assert-equal :*iter-empty* (fwd-iter)))
(let ([fwd-iter, rev-iter] (vec-iter-pair (vec)))
(test::assert-equal :*iter-empty* (fwd-iter))
(test::assert-equal :*iter-empty* (rev-iter)))
iter::vec-iter-rev
Usage: (iter::vec-iter-rev v [SCRATCH] [SCRATCH] idx [SCRATCH] [SCRATCH] iter)
Iterator produces a vector in reverse.
Example:
(import iter)
(let (test-vec-iter (vec-iter-rev [1 2 3]))
(test::assert-equal 3 (test-vec-iter))
(test::assert-equal 2 (test-vec-iter))
(test::assert-equal 1 (test-vec-iter))
(test::assert-equal :*iter-empty* (test-vec-iter))
(set! test-vec-iter (vec-iter-rev (vec)))
(test::assert-equal :*iter-empty* (test-vec-iter)))
math
List of symbols:
%, *, +, -, /, abs, rem, rem_euclid
%
Usage: (% int int)
Remainder from dividing (arg 1) by (arg 2).
Note: Remainder and Modulo are two similar mathematical operations,
called rem
and rem_euclid
in Rust.
This function uses rem
which is the same as the %
operator in C.
With rem
, the sign of the result is the same as the dividend (arg 1).
With rem_euclid
, the sign of the result is always positive.
Example:
(test::assert-equal 0 (% 50 10))
(test::assert-equal 5 (% 55 10))
(test::assert-equal 1 (% 1 2))
(test::assert-equal -1 (% -10 3))
(test::assert-equal 1 (% 10 -3))
(test::assert-equal -1 (% -10 -3))
(test::assert-error (%))
(test::assert-error (% 1))
(test::assert-error (% 1 2 3))
(test::assert-error (% 1 2.0))
*
Usage: (* number*)
Multiply a sequence of numbers. (*) will return 1.
Example:
(test::assert-equal 1 (*))
(test::assert-equal 5 (* 5))
(test::assert-equal 5 (* 1 5))
(test::assert-equal 5.0 (* 1.0 5))
(test::assert-equal 7.5 (* 1.5 5))
(test::assert-equal 7.5 (* 1.5 5.0))
(test::assert-equal 15 (* 3 5))
(test::assert-equal 8 (* 1 2 4))
(test::assert-equal 16 (* 2 2 4))
(test::assert-equal 16.0 (* 2 2.0 4))
(test::assert-equal 16.0 (* 2.0 2.0 4.0))
(test::assert-equal 54.9999999999999 (* 100 0.55))
(test::assert-error (* 1 2 4 "5"))
+
Usage: (+ number*)
Add a sequence of numbers. (+) will return 0.
Example:
(test::assert-equal 0 (+))
(test::assert-equal 5 (+ 5))
(test::assert-equal 10 (+ 5 5))
(test::assert-equal 6 (+ 1 5))
(test::assert-equal 6.5 (+ 1 5.5))
(test::assert-equal 7 (+ 1 2 4))
(test::assert-error (+ 1 2 4 "5"))
-
Usage: (- number+)
Subtract a sequence of numbers. Requires at least one number (negate if only one number).
Example:
(test::assert-error (- 5 "2"))
(test::assert-equal -5 (- 5))
(test::assert-equal -5.0 (- 5.0))
(test::assert-equal -4 (- 1 5))
(test::assert-equal -4.5 (- 1 5.5))
(test::assert-equal 4 (- 10 2 4))
(test::assert-equal 4.5 (- 10 2 3.5))
/
Usage: (/ number+)
Divide a sequence of numbers. Requires at least two numbers.
No Examples
abs
Usage: (abs arg)
Returns absolute value of arg.
Example:
(test::assert-equal 2 (abs 2))
(test::assert-equal 144 (abs -144))
(test::assert-equal 4.53 (abs -4.53))
(test::assert-equal 36028797018963967 (abs -36028797018963967))
rem
Usage: (rem int int)
Remainder from dividing (arg 1) by (arg 2).
Note: Remainder and Modulo are two similar mathematical operations,
called rem
and rem_euclid
in Rust.
This function uses rem
which is the same as the %
operator in C.
With rem
, the sign of the result is the same as the dividend (arg 1).
With rem_euclid
, the sign of the result is always positive.
Example:
(test::assert-equal 0 (rem 50 10))
(test::assert-equal 5 (rem 55 10))
(test::assert-equal 1 (rem 1 2))
(test::assert-equal -1 (rem -10 3))
(test::assert-equal 1 (rem 10 -3))
(test::assert-equal -1 (rem -10 -3))
(test::assert-error (rem))
(test::assert-error (rem 1))
(test::assert-error (rem 1 2 3))
(test::assert-error (rem 1 2.0))
rem_euclid
Usage: (rem_euclid int int)
Least Non-negative number that can be added to a multiple of the divisor (arg 2) to get the dividend (arg 1).
The result should always be 0 <= result < divisor (arg 2).
Note: Remainder and Modulo are two similar mathematical operations,
called rem
and rem_euclid
in Rust.
With rem
, the sign of the result is the same as the dividend (arg 1).
With rem_euclid
, the sign of the result is always positive.
Example:
(test::assert-equal 0 (rem_euclid 50 10))
(test::assert-equal 5 (rem_euclid 55 10))
(test::assert-equal 1 (rem_euclid 1 2))
(test::assert-equal 2 (rem_euclid -10 3))
(test::assert-equal 1 (rem_euclid 10 -3))
(test::assert-equal 2 (rem_euclid -10 -3))
(test::assert-error (rem_euclid))
(test::assert-error (rem_euclid 1))
(test::assert-error (rem_euclid 1 2 3))
(test::assert-error (rem_euclid 1 2.0))
pair
List of symbols:
car
Usage: (car pair)
Return the car (first item) from a pair. If used on a proper list this will be the first element.
Example:
(def tst-pairs-two (list 'x 'y 'z))
(test::assert-equal 'x (car tst-pairs-two))
(test::assert-equal 10 (car '(10)))
(test::assert-equal 9 (car '(9 11 13)))
cdr
Usage: (cdr pair)
Return the cdr (second item) from a pair. If used on a proper list this will be the list minus the first element.
Example:
(def tst-pairs-three (list 'x 'y 'z))
(test::assert-equal '(y z) (cdr tst-pairs-three))
(test::assert-equal nil (cdr '(10)))
(test::assert-equal '(13) (cdr '(9 13)))
(test::assert-equal '(11 13) (cdr '(9 11 13)))
list
Usage: (list item0 item1 .. itemN)
Create a proper list from pairs with items 0 - N.
Example:
(test::assert-equal '(1 2 3) (list 1 2 3))
xar!
Usage: (xar! pair expression)
Destructive form that replaces the car (first item) in a pair with a new expression.
If used on a proper list will replace the first item. Can be used on nil to create a pair (expression . nil).
Example:
(def tst-pairs-three (list 'x 'y 'z))
(test::assert-equal '(x y z) tst-pairs-three)
(test::assert-equal '(s y z) (xar! tst-pairs-three 's))
(test::assert-equal '(s y z) tst-pairs-three)
(def tst-pairs-four (list 't))
(test::assert-equal '(y) (xar! tst-pairs-four 'y))
(test::assert-equal '(y) tst-pairs-four)
xdr!
Usage: (xdr! pair expression)
Destructive form that replaces the cdr (second item) in a pair with a new expression.
If used on a proper list will replace everything after the first item. Can be used on nil to create a pair (nil . expression).
Example:
(def tst-pairs-five (list 'a 'b 'c))
(test::assert-equal '(a b c) tst-pairs-five)
(test::assert-equal '(a y z) (xdr! tst-pairs-five '(y z)))
(test::assert-equal '(a y z) tst-pairs-five)
(def tst-pairs-six (list 'v))
(test::assert-equal (list 'v) tst-pairs-six)
(test::assert-equal '(v . v) (xdr! tst-pairs-six 'v))
(test::assert-equal '(v . v) tst-pairs-six)
random
List of symbols:
probool
Usage: (probool), (probool numerator denominator)
PRObability of a BOOLean.
If no arguments are given, returns #t 1/2 of the time, otherwise takes two integers, numerator and denominator, and returns #t numerator/denominator of the time. Throws an error if denominator is 0. If (>= (/ numerator denominator) 1) probool always returns true. If numerator is 0 probool always returns false.
Example:
(def val0 (probool))
(test::assert-true (or (= #t val0) (= nil val0)))
(def val1 (probool 17 42))
(test::assert-true (or (= #t val1) (= nil val1)))
(test::assert-true (probool 1 1))
(test::assert-false (probool 0 42))
(test::assert-error-msg (probool 0 0) :rand "Denominator can not be zero")
(test::assert-error-msg (probool 0 0 0) :rand "Expected zero or two positive ints")
random
Usage: (random), (random limit)
Returns non-negative number less than limit and of the same type as limit.
Example:
(def rand-int (random 100))
(test::assert-true (and (>= rand-int 0) (< rand-int 100)))
(def rand-float (random 1.0))
(test::assert-true (and (>= rand-float 0.0) (< rand-float 1.0)))
(test::assert-error-msg (random -1) :rand "Expected positive number")
(test::assert-error-msg (random 1 2) :rand "Expected positive number, float or int")
random-str
Usage: (random-str str-length [char-set])
Takes a positive integer, str-length, and one of :hex, :ascii, :alnum, or a string. Returns random string of provided str-length composed of second argument, :hex results in random hex string, :ascii results in random string of all printable ascii characters, :alnum results in random string of all alphanumeric characters, and providing a string results in a random string composed by sampling input.
Example:
(test::assert-error-msg (random-str) :rand "Expected two arguments, length and charset")
(test::assert-error-msg (random-str 10) :rand "Expected two arguments, length and charset")
(test::assert-error-msg (random-str -1 :hex) :rand "Expected positive length")
(test::assert-error-msg (random-str 10 1) :rand "Second argument must be keyword or string")
(test::assert-error-msg (random-str 1 :hexy) :rand "Unknown symbol :hexy")
(test::assert-equal 10 (len (random-str 10 :hex)))
(test::assert-true (str-contains (random-str 42 "⚙") "⚙"))
(test::assert-equal 19 (len (random-str 19 :ascii)))
(test::assert-equal 91 (len (random-str 91 :alnum)))
scripting
List of symbols:
run-script
Usage: (run-script path) -> [last form value]
Read and eval a file (from path- a string).
Example:
(def test-load::test-fn)
(with-temp-file (fn (tmp)
(let (tst-file (fopen tmp :create))
(defer (fclose tst-file))
(fprn tst-file "(with-ns test-load")
(fprn tst-file " (defn test-fn () '(1 2 3)))"))
(run-script tmp)
(test::assert-equal '(1 2 3) (test-load::test-fn))))
sequence
List of symbols:
butlast, first, last, rest, seq-for
butlast
Usage: (butlast obj [SCRATCH] [SCRATCH] new-link)
Produces the provided list minus the last element. Nil if the list is empty or one element.
Example:
(test::assert-equal '(1 2) (butlast '(1 2 3)))
(test::assert-equal [1 2] (butlast [1 2 3]))
(test::assert-equal nil (butlast '(1)))
(test::assert-equal [] (butlast [1]))
(test::assert-equal nil (butlast '()))
(test::assert-equal nil (butlast nil))
(test::assert-equal nil (butlast []))
first
Usage: (first obj)
Produces the first element of the provided list or vector. Nil if the list/vector is nil/empty. Note this is like car that works for lists and vectors.
Example:
(test::assert-equal 1 (first '(1 2 3)))
(test::assert-equal 1 (first [1 2 3]))
(test::assert-equal nil (first '()))
(test::assert-equal nil (first nil))
(test::assert-equal [] (first []))
last
Usage: (last obj [SCRATCH] [SCRATCH] last-list [SCRATCH] [SCRATCH] i)
Produces the last element in a list or vector. Nil if the list/vector is empty.
Example:
(test::assert-equal 3 (last '(1 2 3)))
(test::assert-equal 3 (last [1 2 3]))
(test::assert-equal nil (last '()))
(test::assert-equal nil (last nil))
(test::assert-equal nil (last []))
rest
Usage: (rest obj)
Produces the provided list or vector minus the first element. Nil if the list/vector is nil/empty or one element. Note this is like cdr that works for lists and vectors. This calls vec-slice to create a new vector when called with a vector (i.e. is much more efficient with lists).
Example:
(test::assert-equal '(2 3) (rest '(1 2 3)))
(test::assert-equal [2 3] (rest [1 2 3]))
(test::assert-equal nil (rest '(1)))
(test::assert-equal [] (rest [1]))
(test::assert-equal nil (rest '()))
(test::assert-equal nil (rest nil))
(test::assert-equal [] (rest []))
seq-for
Usage: (seq-for bind in items body [SCRATCH] [SCRATCH] lst)
Loops over each element in a sequence. Simple version that works with lists and vectors, use iterator::for in general.
Example:
(def i 0)
(seq-for x in '(1 2 3 4 5 6) (set! i (+ 1 i)))
(test::assert-equal 6 i)
shell
List of symbols:
args
Usage: args
A vector of the argumants passed to the script. The first argument will be the name of the script.
No Examples
shell-exe
Usage: shell-exe
A string that contains the executable that is running the script.
No Examples
string
List of symbols:
str, str-bytes, str-cat-list, str-contains, str-empty?, str-lower, str-map, str-push!, str-replace, str-split, str-splitn, str-starts-with, str-sub, str-trim, str-trim!, str-upper
str
Usage: (str arg0 ... argN) -> string
Make a new string with its arguments.
Arguments will be turned into strings. If an argument is a process then the output of the process will be captured and put into the string.
Example:
(test::assert-equal "stringsome" (str "string" "some"))
(test::assert-equal "string" (str "string" ""))
(test::assert-equal "string 50" (str "string" " " 50))
str-bytes
Usage: (str-bytes string) -> int
Return number of bytes in a string (may be more then length).
Strings are utf8 so it chars and bytes may not be a one to one match.
Example:
(test::assert-equal 4 (str-bytes "Stau"))
(test::assert-equal 0 (str-bytes ""))
; Note 5 chars and 6 bytes because of the final char.
(test::assert-equal 6 (str-bytes "StauΣ"))
str-cat-list
Usage: (str-cat-list join-str sequence) -> string
Build a string by concatenating a sequence of strings by join-str.
Example:
(test::assert-equal "stringxxxyyyxxxsome" (str-cat-list "xxx" ["string" "yyy" "some"]))
(test::assert-equal "string yyy some" (str-cat-list " " ["string" "yyy" "some"]))
(test::assert-equal "stringyyysome" (str-cat-list "" ["string" "yyy" "some"]))
str-contains
Usage: (str-contains string pattern) -> #t/#f
True if string contains pattern (pattern will be converted to a string first).
Example:
(test::assert-true (str-contains "Stausomething" "Stau"))
(test::assert-false (str-contains "Stausomething" "StaU"))
(test::assert-true (str-contains "Stausomething" "some"))
(test::assert-false (str-contains "Stausomething" "Some"))
(test::assert-true (str-contains "Stausomething" "thing"))
(test::assert-false (str-contains "Stausomething" "Thing"))
(test::assert-true (str-contains "StausomeΣthing" "someΣ"))
str-empty?
Usage: (str-empty? string) -> #t/#f
Is a string empty? Let's find out...
Example:
(test::assert-true (str-empty? ""))
(test::assert-true (str-empty? (str-trim " ")))
(test::assert-false (str-empty? " "))
(test::assert-false (str-empty? "string"))
str-lower
Usage: (str-lower string) -> string
Get all lower case string from a string.
Example:
(test::assert-equal "stau" (str-lower "STAU"))
(test::assert-equal "stau" (str-lower "stau"))
(test::assert-equal "stau" (str-lower "Stau"))
(test::assert-equal "stau" (str-lower "StaU"))
(test::assert-equal "stau" (str-lower "sTaU"))
str-map
Usage: (str-map string lambda) -> string
Make a new string by applying lambda to each char in input string.
Example:
(test::assert-equal "XstringXstrX" (str-map "xstringxstrx" (fn (ch) (if (= "x" ch) "X" ch))))
(def test-str-map (str-map "xstringxstrx" (fn (ch) (if (= "x" ch) "X" ch))))
(test::assert-equal "XstringXstrX" test-str-map)
(test::assert-true (string? test-str-map))
(def test-str-map (str-map (str "xstringxstrx") (fn (ch) (if (= "x" ch) "X" ch))))
(test::assert-equal "XstringXstrX" test-str-map)
(test::assert-true (string? test-str-map))
str-push!
Usage: (str-push! string arg0 ... argN) -> string
Push the args (as strings) onto the string. This is a destructive form.
Arguments will be turned into strings. Returns the string it was given.
Example:
(test::assert-equal "stringsome" (str-push! (str "string") "some"))
(def test-str-push (str "def-string"))
(test::assert-equal "def-stringsome" (str-push! test-str-push "some"))
(test::assert-equal "def-stringsome" test-str-push)
str-replace
Usage: (str-replace string old-pattern new-pattern) -> string
Replace occurrences of second string with third in the first string.
Example:
(test::assert-equal "some yyy string" (str-replace "some xxx string" "xxx" "yyy"))
(test::assert-equal "some yyy string yyy" (str-replace "some xxx string xxx" "xxx" "yyy"))
(test::assert-equal "yyy some yyy string yyy" (str-replace "xxx some xxx string xxx" "xxx" "yyy"))
str-split
Usage: (str-split string split-pattern) -> vector
Use a pattern to split a string (:whitespace to split on whitespace).
Example:
(test::assert-equal ["some" "yyy" "string"] (str-split "somexxxyyyxxxstring" "xxx"))
(test::assert-equal ["some" "yyy" "string" ""] (str-split "somexxxyyyxxxstringxxx" "xxx"))
(test::assert-equal ["" "some" "yyy" "string" ""] (str-split "xxxsomexxxyyyxxxstringxxx" "xxx"))
(test::assert-equal ["some" "yyy" "string"] (str-split "some yyy string" :whitespace))
(test::assert-equal ["somexxxyyyxxxstring"] (str-split "somexxxyyyxxxstring" :whitespace))
(test::assert-equal ["somexxxyyyxxxstring"] (str-split "somexxxyyyxxxstring" "zzz"))
str-splitn
Usage: (str-splitn n split-pattern string) -> vector
Use a pattern to split a string with at most n items.
Example:
(test::assert-equal ["some" "yyy" "string"] (str-splitn 3 "xxx" "somexxxyyyxxxstring"))
(test::assert-equal ["some" "yyy" "string"] (str-splitn 4 "xxx" "somexxxyyyxxxstring"))
(test::assert-equal ["some" "yyy" "stringxxxother"] (str-splitn 3 "xxx" "somexxxyyyxxxstringxxxother"))
(test::assert-equal ["somexxxyyyxxxstringxxxother"] (str-splitn 1 "xxx" "somexxxyyyxxxstringxxxother"))
(test::assert-equal [] (str-splitn 0 "xxx" "somexxxyyyxxxstringxxxzero"))
str-starts-with
Usage: (str-starts-with string pattern) -> #t/#f
True if string start with pattern.
Example:
(test::assert-true (str-starts-with "Stausomething" "Stau"))
(test::assert-false (str-starts-with "Stausomething" "StaU"))
str-sub
Usage: (str-sub string start [length]) -> string
Return a substring from a string given start (0 based) and optional length. If length is 0 or not provided produces the rest of the string from start to string end.
Example:
(test::assert-equal "string" (str-sub "stringxxxyyyxxxsome" 0 6))
(test::assert-equal "some" (str-sub "stringxxxyyyxxxsome" 15 4))
(test::assert-equal "yyy" (str-sub "stringxxxyyyxxxsome" 9 3))
(test::assert-equal "some" (str-sub "stringxxxyyyxxxsome" 15))
str-trim
Usage: (str-trim string [:right | :left]) -> string
Trim right and/or left whitespace from string. With no optional keywork trims both, otherwise :right or :left specify right or left trimming.
Example:
(test::assert-equal "some string" (str-trim " some string"))
(test::assert-equal "some string" (str-trim " some string "))
(test::assert-equal "some string" (str-trim (str " some string ")))
(test::assert-equal "some string" (str-trim "some string "))
(test::assert-equal "some string" (str-trim "some string"))
(test::assert-equal " some string" (str-trim " some string" :right))
(test::assert-equal " some string" (str-trim " some string " :right))
(test::assert-equal " some string" (str-trim (str " some string ") :right))
(test::assert-equal "some string" (str-trim "some string " :right))
(test::assert-equal "some string" (str-trim "some string" :right))
(test::assert-equal "some string" (str-trim " some string" :left))
(test::assert-equal "some string " (str-trim " some string " :left))
(test::assert-equal "some string " (str-trim (str " some string ") :left))
(test::assert-equal "some string " (str-trim "some string " :left))
(test::assert-equal "some string" (str-trim "some string" :left))
str-trim!
Usage: (str-trim! string [:right | :left]) -> string
Trim right and/or left whitespace from string in place. With no optional keywork trims both, otherwise :right or :left specify right or left trimming.
This is a destructive operation (unlike str-trim) and requires an actual non-const string as it's first argument. It returns this string on success.
Example:
(test::assert-equal "some string" (str-trim! (str " some string")))
(test::assert-equal "some string" (str-trim! (str " some string ")))
(test::assert-equal "some string" (str-trim! (str (str " some string "))))
(test::assert-equal "some string" (str-trim! (str "some string ")))
(test::assert-equal "some string" (str-trim! (str "some string")))
(test::assert-equal " some string" (str-trim! (str " some string") :right))
(test::assert-equal " some string" (str-trim! (str " some string ") :right))
(test::assert-equal " some string" (str-trim! (str (str " some string ")) :right))
(test::assert-equal "some string" (str-trim! (str "some string ") :right))
(test::assert-equal "some string" (str-trim! (str "some string") :right))
(test::assert-equal "some string" (str-trim! (str " some string") :left))
(test::assert-equal "some string " (str-trim! (str " some string ") :left))
(test::assert-equal "some string " (str-trim! (str (str " some string ")) :left))
(test::assert-equal "some string " (str-trim! (str "some string ") :left))
(test::assert-equal "some string" (str-trim! (str "some string") :left))
str-upper
Usage: (str-upper string) -> string
Get all upper case string from a string.
Example:
(test::assert-equal "STAU" (str-upper "STAU"))
(test::assert-equal "STAU" (str-upper "stau"))
(test::assert-equal "STAU" (str-upper "Stau"))
(test::assert-equal "STAU" (str-upper "StaU"))
(test::assert-equal "STAU" (str-upper "sTaU"))
system
List of symbols:
sleep
Usage: (sleep milliseconds) -> nil
Sleep for at least the provided milliseconds (must be a positive integer), otherwise function will no-op.
No Examples
test
List of symbols:
test::assert-equal, test::assert-error, test::assert-error-msg, test::assert-false, test::assert-not-equal, test::assert-true
test::assert-equal
Usage: (test::assert-equal expected-val right-val body)
Asserts the two values are identical.
No Examples
test::assert-error
Usage: (test::assert-error val body)
Asserts the value is an error
No Examples
test::assert-error-msg
Usage: (test::assert-error-msg form key msg)
Test asserts an error is thrown with a given key and message.
Example:
(test::assert-error-msg (err "error thrown") :error "error thrown")
test::assert-false
Usage: (test::assert-false val body)
Asserts the value is false
No Examples
test::assert-not-equal
Usage: (test::assert-not-equal expected-val right-val body)
Asserts the two values are not identical.
No Examples
test::assert-true
Usage: (test::assert-true val body)
Asserts the value is true.
No Examples
type
List of symbols:
callable?, char?, err?, io?, list?, nil?, ok?, pair?, seq?, str->float, string?, symbol?, vec?
callable?
Usage: (callable? v [SCRATCH] [SCRATCH] t)
Usage: (callable? to-test)
No Examples
char?
Usage: (char? expression)
True if the expression is a char, false otherwise.
Example:
(test::assert-true (char? \a))
(test::assert-false (char? 1))
(test::assert-false (char? "a"))
err?
Usage: (err? expression)
True if the expression is an error, false otherwise.
Example:
(test::assert-true (err? (mk-err :arr "test")))
(test::assert-false (err? nil))
io?
Usage: (io? expression)
True if the expression is an IO object (file), false otherwise.
Example:
(def iotst (fopen "/tmp/iotst" :create))
(test::assert-true (io? iotst))
(test::assert-false (io? 1))
(test::assert-false (io? '(1 2 3)))
(test::assert-false (io? (list)))
list?
Usage: (list? expression)
True if the expression is a list, false otherwise.
Example:
(test::assert-true (list? '(1 2 3)) "reader macro")
(test::assert-true (list? (list 1 2 3)) "list")
(test::assert-false (list? 1))
(test::assert-false (list? [1 2 3]))
(test::assert-false (list? []))
(test::assert-false (list? '(1 . 2)))
nil?
Usage: (nil? v)
True if the expression is nil, false otherwise
Example:
(test::assert-true (nil? ()))
(test::assert-true (nil? '()))
(test::assert-true (nil? nil))
(test::assert-true (nil? (list)))
(test::assert-false (nil? #f))
ok?
Usage: (ok? expression)
True if the expression is NOT an error, false if it is an error.
Example:
(test::assert-false (ok? (mk-err :arr "test")))
(test::assert-true (ok? nil))
pair?
Usage: (pair? expression)
True if the expression is a pair, false otherwise.
Example:
(test::assert-true (pair? '(1 . 2)) "reader macro")
(test::assert-true (pair? (cons 1 2)) "cons")
(test::assert-true (pair? '(1 2)))
(test::assert-false (pair? 1))
(test::assert-false (pair? [1 2 3]))
(test::assert-false (pair? (vec)))
seq?
Usage: (seq? expression)
True if expression is a list or vector, false otherwise.
Example:
(test::assert-true (seq? '(1 2 3)))
(test::assert-true (seq? [1 2 3]))
(test::assert-true (seq? []))
(test::assert-false (seq? "aaa"))
(test::assert-false (seq? 1))
str->float
Usage: (str->float string) -> float
If string is a valid representation of a float return that float. Error if not.
Example:
(test::assert-equal 0.0 (str->float "0"))
(test::assert-equal 10.0 (str->float "10.0"))
(test::assert-equal 10.5 (str->float "10.5"))
(test::assert-equal 101.0 (str->float "101"))
(test::assert-equal -101.95 (str->float "-101.95"))
(test::assert-error (str->float "not int"))
(test::assert-error (str->float "--10"))
string?
Usage: (string? expression)
True if the expression is a string, false otherwise.
Example:
(test::assert-true (string? "string"))
(test::assert-false (string? 1))
symbol?
Usage: (symbol? expression)
True if the expression is a symbol, false otherwise.
Example:
(test::assert-true (symbol? 'symbol))
(test::assert-false (symbol? 1))
vec?
Usage: (vec? expression)
True if the expression is a vector, false otherwise.
Example:
(test::assert-true (vec? [1 2 3]) "reader macro")
(test::assert-true (vec? (make-vec)) "make-vec")
(test::assert-true (vec? (vec 1 2 3)) "vec")
(test::assert-false (vec? 1))
(test::assert-false (vec? '(1 2 3)))
(test::assert-false (vec? (list)))
vector
List of symbols:
make-vec, vec, vec->list, vec-pop!, vec-push!, vec-slice
make-vec
Usage: (make-vec capacity default)
Make a new vector with capacity and default item(s).
Example:
(test::assert-equal [] (make-vec))
(test::assert-equal ['x 'x 'x] (make-vec 3 'x))
(test::assert-equal [nil nil nil nil nil] (make-vec 5 nil))
(test::assert-equal [] (make-vec 5))
vec
Usage: (vec item1 item2 .. itemN)
Make a new vector with items.
Example:
(test::assert-equal [] (vec))
(test::assert-equal [1 2 3] (vec 1 2 3))
vec->list
Usage: (vec->list vector)
Convert a vector to a list.
No Examples
vec-pop!
Usage: (vec-pop! vector) -> object
Pops the last object off of the end of the vector. This is destructive!
Example:
(def test-pop-vec (vec 1 2 3))
(test::assert-equal 3 (vec-pop! test-pop-vec))
(test::assert-equal [1 2] test-pop-vec)
(test::assert-equal 2 (vec-pop! test-pop-vec))
(test::assert-equal [1] test-pop-vec)
(test::assert-equal 1 (vec-pop! test-pop-vec))
(test::assert-equal [] test-pop-vec)
vec-push!
Usage: (vec-push! vector object) -> vector
Pushes the provided object onto the end of the vector. This is destructive!
Example:
(def test-push-vec (vec))
(test::assert-equal [1] (vec-push! test-push-vec 1))
(test::assert-equal [1] test-push-vec)
(test::assert-equal [1 2] (vec-push! test-push-vec 2))
(test::assert-equal [1 2] test-push-vec)
(test::assert-equal [1 2 3] (vec-push! test-push-vec 3))
(test::assert-equal [1 2 3] test-push-vec)
vec-slice
Usage: (vec-slice vector start end)
Returns a slice of a vector (0 based indexes, end is exclusive).
Example:
(test::assert-equal [5 6] (vec-slice [1 2 3 4 5 6] 4 6))
(test::assert-equal [1 2 3] (vec-slice [1 2 3 4 5 6] 0 3))
(test::assert-equal [3 4 5] (vec-slice [1 2 3 4 5 6] 2 5))
(test::assert-equal [3 4 5 6] (vec-slice [1 2 3 4 5 6] 2))
tracking function Parity Between sl-sh and slosh
Forms yet to be implemented: 325
Forms implemented or skipped: 196
? | Slosh Form | Notes |
---|---|---|
✅ | % | |
✅ | * | |
✅ | *active-ns* | Renamed to: *ns* |
✅ | *bg-black* | |
✅ | *bg-blue* | |
✅ | *bg-cyan* | |
✅ | *bg-default* | |
✅ | *bg-green* | |
✅ | *bg-magenta* | |
✅ | *bg-red* | |
✅ | *bg-white* | |
✅ | *bg-yellow* | |
☑️ | *collection-src* | Not necessary in slosh. |
☑️ | *core-src* | Not necessary in slosh. |
✅ | *euid* | |
❌ | *euler* | |
✅ | *fg-black* | |
✅ | *fg-blue* | |
✅ | *fg-cyan* | |
✅ | *fg-default* | |
✅ | *fg-green* | |
✅ | *fg-magenta* | |
✅ | *fg-red* | |
✅ | *fg-white* | |
✅ | *fg-yellow* | |
☑️ | *getopts-log* | Not necessary in slosh. |
☑️ | *getopts-src* | Not necessary in slosh. |
☑️ | *iterator-src* | Not necessary in slosh. |
❌ | *last-command* | |
✅ | *last-status* | |
❌ | *lib-src* | |
❌ | *load-path* | |
✅ | *ns* | |
❌ | *ns-exports* | |
❌ | *pi* | |
❌ | *read-table* | |
❌ | *read-table-terminal* | |
❌ | *repl-settings* | |
❌ | *run-script* | |
☑️ | *seq-src* | Not necessary in slosh. |
☑️ | *shell-read-src* | Not necessary in slosh. |
☑️ | *shell-src* | Not necessary in slosh. |
☑️ | *slsh-std-src* | Not necessary in slosh. |
☑️ | *slshrc-src* | Not necessary in slosh. |
❌ | *std-lib-exported-syms-hash* | |
❌ | *std-lib-namespaces* | |
❌ | *std-lib-syms-hash* | |
❌ | *stderr* | |
❌ | *stdin* | |
❌ | *stdout* | |
❌ | *string-read-table* | |
☑️ | *struct-src* | Not necessary in slosh. |
☑️ | *test-src* | Not necessary in slosh. |
✅ | *uid* | |
❌ | =+ | |
✅ | - | |
❌ | -> | |
❌ | ->> | |
✅ | / | |
❌ | 2pow | |
✅ | < | |
✅ | <= | |
✅ | = | |
✅ | > | |
✅ | >= | |
❌ | ^ns-stack-xyz^ | |
❌ | __prompt | |
✅ | abs | |
❌ | alias | |
❌ | alias? | |
✅ | and | |
❌ | and-let* | |
❌ | append | |
❌ | append-iter | |
❌ | append-to! | |
✅ | apply | |
❌ | apply-defaults | |
❌ | arccos | |
❌ | arcsin | |
❌ | arctan | |
❌ | args | |
❌ | arity-zero-can-not-be-required | |
❌ | assert-equal | |
❌ | assert-error | |
❌ | assert-error-msg | |
❌ | assert-false | |
❌ | assert-includes | |
❌ | assert-not-equal | |
❌ | assert-not-includes | |
❌ | assert-true | |
✅ | back-quote | |
❌ | bg | |
✅ | bg-color-rgb | |
✅ | block | |
❌ | boolean? | |
❌ | builtin? | |
✅ | butlast | |
❌ | caaar | |
❌ | caadr | |
❌ | caar | |
❌ | cadar | |
❌ | cadddr | |
❌ | caddr | |
❌ | cadr | |
✅ | callable? | |
✅ | car | |
✅ | cd | |
❌ | cdaar | |
❌ | cdadr | |
❌ | cdar | |
❌ | cddar | |
❌ | cdddr | |
❌ | cddr | |
✅ | cdr | |
❌ | ceil | |
❌ | chain | |
❌ | chain-and | |
❌ | chain-when | |
❌ | char->int | |
✅ | char-lower | |
✅ | char-upper | |
✅ | char-whitespace? | |
✅ | char? | |
❌ | check | |
❌ | check-custom | |
❌ | clear-dirs | |
❌ | close | |
❌ | codepoints | |
❌ | collate-fs-changes | |
❌ | collect | |
❌ | collect-copy | |
❌ | collect-str | |
❌ | collect-vec | |
✅ | cond | |
❌ | consume-comment | |
❌ | consume-whitespace | |
❌ | cos | |
✅ | dec! | |
✅ | def | |
✅ | def? | |
✅ | defmacro | |
✅ | defn | |
☑️ | defstruct | Will consider other implementations if struct-like functionality if desired. |
☑️ | deftrait | Will consider other implementations if trait-like functionality is desired. |
❌ | dirs | |
✅ | do | |
❌ | do-unstr | |
✅ | doc | |
☑️ | doc-raw | Possible now with command: (get-prop 'symbol :doc-string) |
✅ | dotimes | |
✅ | dotimes-i | |
☑️ | double-ended-iter? | Not necessary in slosh. |
☑️ | double-ended-iterator | Not necessary in slosh. |
✅ | dyn | |
❌ | empty-seq? | |
✅ | empty? | |
❌ | enforce-constrains | |
❌ | epoch | |
✅ | eprint | Renamed to: epr |
✅ | eprintln | Renamed to: eprn |
✅ | err | |
❌ | err> | |
❌ | err>> | |
❌ | err>null | |
❌ | error-stack-off | |
❌ | error-stack-on | |
✅ | eval | |
❌ | exit | |
❌ | exp | |
❌ | expand-brace | |
❌ | expand-dollar | |
✅ | expand-macro | |
❌ | expand-macro-all | |
❌ | expand-macro1 | |
❌ | expand-tilde | |
❌ | export | |
❌ | false? | |
❌ | falsey? | |
❌ | fc | |
❌ | fg | |
✅ | fg-color-rgb | |
❌ | file-iter | |
❌ | file? | |
❌ | filter | |
❌ | filter-iter | |
❌ | find-symbol | |
✅ | first | |
❌ | first-quartile | |
❌ | fix-one-arg-bindings | |
❌ | flatten-args | |
❌ | float->int | |
❌ | float? | |
❌ | floor | |
✅ | flush | Renamed to: fflush |
✅ | fn | |
❌ | fn-to-predicate | |
❌ | fncall | |
❌ | for | |
❌ | for-i | |
❌ | fork | |
❌ | format | |
❌ | fract | |
✅ | fs-accessed | |
✅ | fs-base | |
✅ | fs-crawl | |
✅ | fs-dir? | |
✅ | fs-exists? | |
✅ | fs-file? | |
✅ | fs-len | |
✅ | fs-modified | |
❌ | fs-notify | |
✅ | fs-parent | |
✅ | fs-rm | |
✅ | fs-same? | |
❌ | func? | |
✅ | gensym | |
❌ | get-arity | |
❌ | get-dirs | |
❌ | get-env | |
✅ | get-error | |
❌ | get-home | |
❌ | get-next-params | |
❌ | get-pid | |
✅ | get-rgb-seq | |
✅ | get-temp | |
✅ | get-temp-file | |
❌ | get_pwd | |
❌ | getopts | |
❌ | getopts-bad-first-arg | |
❌ | getopts-bad-option-arity | |
❌ | getopts-help | |
❌ | getopts-help--options-map-is-map | |
❌ | getopts-illegal-option | |
❌ | getopts-invalid-type-function | |
❌ | getopts-options-map-is-map | |
❌ | getopts-type-error-message | |
✅ | glob | |
❌ | handle-last-command | |
❌ | handle-process | |
❌ | hash-clear! | |
❌ | hash-get | |
❌ | hash-haskey | |
✅ | hash-keys | |
✅ | hash-remove! | |
❌ | hash-set! | |
❌ | hash? | |
❌ | history-context | |
❌ | history-empty? | |
❌ | history-length | |
❌ | history-nth | |
❌ | history-push | |
❌ | history-push-throwaway | |
✅ | identity | |
✅ | if | |
✅ | import | |
✅ | in? | |
✅ | inc! | |
❌ | int->float | |
❌ | int? | |
❌ | interleave | |
❌ | interleave-iter | |
❌ | intern-stats | |
❌ | is-getopts-option-string | |
❌ | is-multi-char-arg | |
❌ | is-multi-single-char-args | |
❌ | is-single-char-arg | |
❌ | iter | |
❌ | iter-or-single | |
❌ | iter? | |
❌ | iterator | |
❌ | jobs | |
❌ | join | |
❌ | lambda? | |
✅ | last | |
❌ | len0? | |
❌ | len>0? | |
❌ | length | |
✅ | let | |
❌ | let* | |
❌ | let-env | |
✅ | list | |
❌ | list-iter | |
❌ | list-vec? | |
✅ | list? | |
❌ | lists= | |
❌ | lne | |
✅ | load | |
❌ | log | |
❌ | log2 | |
❌ | logger | |
✅ | loop | |
✅ | macro | |
❌ | macro? | |
✅ | make-hash | |
❌ | make-hash-with-keys | |
❌ | make-regex | |
✅ | make-vec | |
❌ | map | |
❌ | map-iter | |
✅ | match | |
❌ | max | |
❌ | maybe-docstring? | |
❌ | maybe-glob? | |
❌ | mean | |
❌ | median | |
❌ | meld | |
❌ | meld-iter | |
❌ | meta-add-tags | |
❌ | meta-column-no | |
❌ | meta-file-name | |
❌ | meta-line-no | |
❌ | meta-tag? | |
❌ | method | |
❌ | min | |
❌ | mkli | |
❌ | mode | |
❌ | next! | |
✅ | nil? | |
❌ | non-empty-seq? | |
❌ | none? | |
✅ | not | |
❌ | ns-auto-export | |
❌ | ns-create | |
❌ | ns-enter | |
❌ | ns-exists? | |
☑️ | ns-export | Everything in slosh is exported by default. |
✅ | ns-import | Renamed to: import |
❌ | ns-list | |
☑️ | ns-pop | Not necessary in slosh. |
☑️ | ns-push | Not necessary in slosh. |
❌ | ns-symbols | |
✅ | nsubstitute! | |
❌ | nth | |
❌ | null | |
❌ | nyi | |
✅ | occurs | |
✅ | open | Renamed to: fopen |
✅ | or | |
❌ | out-err> | |
❌ | out-err>> | |
❌ | out-err>null | |
❌ | out> | |
❌ | out>> | |
❌ | out>null | |
☑️ | pair= | Do not need separate equals specifier for pair. |
✅ | pair? | |
❌ | path_list_trunc | |
❌ | pid | |
❌ | pipe | |
☑️ | pipe-err | Not necessary in slosh. |
❌ | popd | |
❌ | pow | |
✅ | print | Renamed to: pr |
☑️ | print-error | Now one drops in to the debugger when an error occurs. |
✅ | println | Renamed to: prn |
✅ | probool | |
❌ | process? | |
❌ | prompt | |
❌ | pushd | |
❌ | qsort | |
✅ | quote | |
✅ | random | |
✅ | random-str | |
❌ | range | |
❌ | range-iter | |
❌ | re-color | |
❌ | re-find | |
❌ | re-find-all | |
❌ | re-match | |
❌ | re-replace | |
✅ | read | |
✅ | read-all | |
✅ | read-line | |
❌ | read-list | |
❌ | read-string | |
❌ | read-var | |
❌ | read-var-bracket | |
❌ | reader-macro-dot | |
❌ | reap-jobs | |
✅ | recur | |
❌ | redir&> | |
❌ | redir&>> | |
❌ | redir2> | |
❌ | redir2>> | |
❌ | redir> | |
❌ | redir>> | |
❌ | reduce | |
❌ | reduce-times | |
✅ | ref | |
❌ | regex? | |
❌ | register-alias | |
❌ | repeat | |
❌ | repeat-iter | |
❌ | repl | |
❌ | repl-eof | |
❌ | repl-line | |
❌ | required-argument | |
✅ | rest | |
❌ | return-from | |
✅ | reverse | |
❌ | reverse-iter | |
❌ | rm-esc | |
❌ | round | |
❌ | run-bg-first | |
❌ | run-bg-prep-args | |
❌ | run-example | |
❌ | run-ns-example | |
✅ | seq-for | |
✅ | seq? | |
✅ | set! | |
❌ | set-dirs-max | |
❌ | set_prompt_tail | |
❌ | setnth! | |
❌ | setup-chainer | |
❌ | shell-read | |
❌ | shell-read-int | |
❌ | sin | |
❌ | single-iter | |
✅ | sleep | |
❌ | slice | |
❌ | slice-iter | |
❌ | some? | |
❌ | sqrt | |
❌ | std-dev | |
✅ | str | |
✅ | str->float | |
✅ | str->int | |
❌ | str-append | |
✅ | str-bytes | |
✅ | str-cat-list | |
❌ | str-clear! | |
✅ | str-contains | |
✅ | str-empty? | |
❌ | str-iter-empty? | |
❌ | str-iter-next! | |
❌ | str-iter-peek | |
❌ | str-iter-start | |
✅ | str-lower | |
❌ | str-ltrim | |
✅ | str-map | |
❌ | str-nth | |
✅ | str-push! | |
✅ | str-replace | |
❌ | str-rsplit | |
❌ | str-rsplitn | |
❌ | str-rtrim | |
✅ | str-split | |
✅ | str-splitn | |
✅ | str-starts-with | |
✅ | str-sub | |
✅ | str-trim | |
✅ | str-upper | |
❌ | string-iter | |
✅ | string? | |
✅ | substitute | |
❌ | summary-stats | |
❌ | supported-types-map | |
❌ | sym | |
❌ | sym->str | |
✅ | symbol? | |
✅ | syntax-off | |
✅ | syntax-on | |
❌ | sys-apply | |
✅ | sys-command? | |
✅ | syscall | Renamed to: sh |
❌ | take | |
❌ | take-iter | |
❌ | tan | |
✅ | temp-dir | |
❌ | third-quartile | |
❌ | time | |
❌ | timer | |
❌ | to-degrees | |
❌ | to-radians | |
✅ | tok-default-color | |
✅ | tok-invalid-color | |
✅ | tok-slsh-fcn-color | |
✅ | tok-slsh-form-color | |
✅ | tok-string-color | |
✅ | tok-sys-alias-color | |
✅ | tok-sys-command-color | |
❌ | token-delim | |
❌ | true? | |
✅ | type | |
❌ | umask | |
❌ | unalias | |
❌ | undef | |
❌ | unexport | |
❌ | unregister-alias | |
❌ | unwind-protect | |
❌ | valid-first-arg? | |
❌ | values | |
❌ | values-length | |
❌ | values-nth | |
❌ | values? | |
❌ | var | |
❌ | var-or-env | |
✅ | vec | |
❌ | vec-clear! | |
❌ | vec-empty? | |
❌ | vec-insert! | |
❌ | vec-iter | |
❌ | vec-nth | |
✅ | vec-pop! | |
✅ | vec-push! | |
❌ | vec-remove! | |
❌ | vec-set! | |
✅ | vec-slice | |
✅ | vec? | |
☑️ | verify-all-options-valid | Leftover from getopts implementation. |
☑️ | verify-arity | Leftover from getopts implementation. |
✅ | version | |
❌ | wait | |
✅ | when | |
❌ | with-padding | |
✅ | with-temp | |
✅ | with-temp-file | |
❌ | write-line | |
❌ | write-string | |
✅ | xar! | |
✅ | xdr! |
[Slosh Rust Docs]
[All Rust Docs]
[Legacy sl-sh Documentation]