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) is true (comparing an int to a byte)

    • (= 2 2.0) is false (comparing an int to a float)

    • (= 0.0 -0.0) is true

    • (= NaN NaN) is false

    • state.rs defines special forms object with key equal mapped to the name =.

    • compile.rs matches on env.specials().equal and generates opcode EQUAL

    • exec_loop.rs maps opcode EQUAL to function is_equal

    • vm.rs is_equal converts each arg to a Value (in case it needs to be dereferenced) and calls is_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 with Value::get_int and compares with rust native ==
      • check if both args are numbers (Byte, Int, or Float) and if so, converts both to f64 with Value::get_float and compares with rust native ==
  • ==

    • (== 1 1.0) is true (comparing an int to a float)

    • (== 0.0 -0.0) is true

    • (== NaN NaN) is false

    • 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 key numeq mapped to the name ==.

    • compile.rs calls compile_list which calls compile_special which calls compile_math in compile_math.rs

    • compile_math.rs compile_math matches on env.specials().numeq and generates opcode NUMEQ

    • exec_loop.rs maps opcode NUMEQ to function compare_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 to f64 with get_primitive_float macro and uses the comparator
      • checks if either argument is a Int and if so, converts both to i64 with get_primitive_int macro and uses the comparator
  • identical?

    • (identical? 1 1) is true

    • (identical? 1 1.0) is false (different types)

    • (identical? 0.0 -0.0) is false (comparing floats with different bit patterns)

    • (identical? NaN NaN) might be true or false. 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 key eq mapped to the name identical?.

    • compile.rs matches on env.specials().eq and generates opcode EQ

    • exec_loop.rs maps opcode EQ to function is_identical

    • vm.rs is_identical converts each arg to a Value (in case it needs to be dereferenced) and compares val1 == val2 which uses Value::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
  • not=

    • defined in vm/core.slosh as the negation of =
  • not==

    • defined in vm/core.slosh as the negation of ==

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

  1. Start with the lisp program (1.1)
  2. 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 a Value
  1. In main.rs
  • The exec_expression function calls pass1
  • And then it calls compile
  1. 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
  1. In state.rs
  • add_constant is called which inserts the Value of 1.1 into pub struct CompileState's pub 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
  1. In float_56.rs
  • F56 impl's the Hash trait and has a custom implementation of hash that converts the F56 to a u64 and then hashes the result
  1. In compile.rs
  • Recall that exec_expression called compile after pass1
  • calls to compile trickle down into compile_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

->key, ->sym, def?, ref

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

fs-meta

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

car, cdr, list, xar!, xdr!

Section: random

probool, random, random-str

Section: scripting

run-script

Section: sequence

butlast, first, last, rest, seq-for

Section: shell

args, shell-exe

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

sleep

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, ->sym, def?, ref

->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

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, cdr, list, xar!, xdr!

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, random, random-str

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

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, shell-exe

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

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 FormNotes
%
*
*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]