summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/api/index.txt91
-rw-r--r--doc/api/libbio/index.txt416
-rw-r--r--doc/api/libdate/formatting.txt99
-rw-r--r--doc/api/libdate/index.txt95
-rw-r--r--doc/api/libdate/manipulation.txt130
-rw-r--r--doc/api/libdate/parsing.txt74
-rw-r--r--doc/api/libdate/types.txt71
-rw-r--r--doc/api/libinifile/index.txt135
-rw-r--r--doc/api/libregex/index.txt279
-rw-r--r--doc/api/libstd/algorithms.txt129
-rw-r--r--doc/api/libstd/alloc.txt160
-rw-r--r--doc/api/libstd/bigint.txt208
-rw-r--r--doc/api/libstd/cli.txt290
-rw-r--r--doc/api/libstd/datastruct.txt181
-rw-r--r--doc/api/libstd/dns.txt102
-rw-r--r--doc/api/libstd/err.txt116
-rw-r--r--doc/api/libstd/files.txt369
-rw-r--r--doc/api/libstd/fmt.txt201
-rw-r--r--doc/api/libstd/index.txt711
-rw-r--r--doc/api/libstd/iterutil.txt100
-rw-r--r--doc/api/libstd/misc.txt107
-rw-r--r--doc/api/libstd/networking.txt181
-rw-r--r--doc/api/libstd/os.txt228
-rw-r--r--doc/api/libstd/randomness.txt88
-rw-r--r--doc/api/libstd/slices.txt71
-rw-r--r--doc/api/libstd/strings.txt205
-rw-r--r--doc/api/libstd/unicode.txt133
-rw-r--r--doc/api/libstd/varargs.txt189
-rw-r--r--doc/api/libtestr/index.txt64
-rw-r--r--doc/api/libthread/index.txt115
30 files changed, 5338 insertions, 0 deletions
diff --git a/doc/api/index.txt b/doc/api/index.txt
new file mode 100644
index 0000000..cac4744
--- /dev/null
+++ b/doc/api/index.txt
@@ -0,0 +1,91 @@
+{
+ title: API Reference
+ description: Myrddin API Reference
+}
+
+This is the page where the Myrddin language is documented. You want to know
+all the boring, dull details? Well, they're here.
+
+APIs
+----
+
+### [Libstd](libstd)
+
+The standard library. You'll probably be using it in all your code, because
+it's the standard. It's intended to cover a good portion of the functionality
+that you'll need for any program. It's a bit of a grab bag, but can be roughly
+categorized into the following subsections:
+
+- [Memory Allocation](libstd/alloc): All of your memory allocation needs.
+- [Error Handling](libstd/err): Convenient utilities for aborting your program.
+- [OS Interfaces](libstd/os): Ask not what you can do for your OS, ask what
+ your OS can do for you.
+- [File Handling](libstd/files): Sometimes data just wants to persist.
+- [Networking](libstd/networking): Communicating with other sytems isn't just
+ a fad.
+- [Command Line Parsing](libstd/cli): Makes it easy to parse options from the
+ commad line.
+- [Formatted Output](libstd/fmt): I/O. Without monads.
+- [Variadic Arguments](libstd/varargs): If you want ugly APIs, we've got you
+ covered.
+- [Slice manipulation](libstd/slices): Some generic functions. Easy to write
+ yourself, but a bit tedious.
+- [String Manipulation](libstd/strings): Some unicode aware string poking.
+- [Unicode](libstd/unicode): Wait a second, all languages aren't english?
+- [Pervasive Data Structures](libstd/datastruct): At least, I use them a lot.
+ If you use them too, we've got you covered.
+- [Misc](libstd/misc): Random crap that doesn't really fit into a category.
+
+### [Libsys](libsys)
+
+Libsys is a direct interface to system calls provided by the platform. It is
+
+- [Linux Syscalls](libsys/linux)
+- [OSX Syscalls](libsys/osx)
+- [FreeBSD Syscalls](libsys/freebsd)
+- [Plan 9 Syscalls](libsys/plan9)
+
+### [Libbio](/myrddin/doc/libbio)
+
+This is a buffered IO library. It allows for many small reads and writes to be
+done with better performance than writing a system call for each one. On top
+of that, it allows for more convenient interfaces to handle linewise or
+delimited input, where peeking at the input stream may be necessary.
+
+### [Libregex](/myrddin/doc/libregex)
+
+This is a regex library, as implied by the name. It implements a simple but
+powerful regex syntax, with full unicode support. It also exposes the regex
+syntax tree if needed, which is useful for code that wants custom regex
+handling, but wants to remain consistent with the regexes in hairless.
+
+### [Libcryptohash](/myrddin/doc/libcryptohash)
+
+This is a library that handles cryptographic hashes. It implements many of the
+most common cryptographic hashes, and provides a fairly consistent interface
+to it.
+
+### [Libdate](/myrddin/doc/libdate)
+
+Libdate provides a fairly complete interface for manipulating dates, times,
+and timezones. It handles adding durations and periods to dates, formatting
+and parsing dates.
+
+### [Libthread](/myrddin/doc/libthread)
+
+Libthread is currently half assed and broken, and it doesn't work very well
+with libstd, which is not yet thread aware. This needs work.
+
+Utilities
+---------
+
+### [Mbld](/myrddin/doc/mbld)
+
+Mbld is the Myrddin build tool. It knows how to handle source and library
+dependencies, as well as build generated sources.
+
+### [Hairless](/myrddin/doc/mbld)
+
+Hairless is the Myrddin parser generator. It's currently in a half-finished
+state, but is quit
+
diff --git a/doc/api/libbio/index.txt b/doc/api/libbio/index.txt
new file mode 100644
index 0000000..c0099b4
--- /dev/null
+++ b/doc/api/libbio/index.txt
@@ -0,0 +1,416 @@
+{
+ title: libbio
+ description: BIO library description
+}
+
+Myrddin's BIO library is used for buffered input and output. It is a fairly
+simple library that handles reading and writing from file descriptors.
+
+The concepts in libbio should be familiar to anyone that has used a buffered
+IO library in most languages. The usual concepts are supported: Files,
+formatted output, binary and ascii reads, and so on. There are also some
+utility functions to deal with reading integers in a known endianness.
+
+One thing to keep in mind with libbio that it does not attempt to flush
+buffers on program exit, which means that any unwritten, unflushed data
+will be lost. Closing the bio files will prevent this issue.
+
+Summary
+-------
+
+ pkg bio =
+ type mode
+ const Rd : mode
+ const Wr : mode
+ const Rw : mode
+
+ type file = struct
+ ;;
+
+ type lineiter
+
+ type status(@a) = union
+ `Eof
+ `Ok @a
+ `Err ioerr
+ ;;
+
+ type ioerr = union
+ `Ebadfile
+ `Ebadbuf
+ `Ebadfd
+ `Eioerr
+ ;;
+
+ impl iterable lineiter
+
+ /* creation */
+ const mkfile : (fd : std.fd, mode : mode -> file#)
+ const open : (path : byte[:], mode : mode -> std.result(file#, byte[:]))
+ const dial : (srv : byte[:], mode : mode -> std.result(file#, byte[:]))
+ const create : (path : byte[:], mode : mode, perm : int -> std.result(file#, byte[:]))
+ const close : (f : file# -> bool)
+ const free : (f : file# -> void)
+
+ /* basic i/o. Returns sub-buffer when applicable. */
+ const write : (f : file#, src : byte[:] -> status(std.size))
+ const read : (f : file#, dst : byte[:] -> status(byte[:]))
+ const flush : (f : file# -> bool)
+
+ /* seeking */
+ const seek : (f : file#, std.off -> std.result(std.off, ioerr))
+
+ /* single unit operations */
+ const putb : (f : file#, b : byte -> status(std.size))
+ const putc : (f : file#, c : char -> status(std.size))
+ const getb : (f : file# -> status(byte))
+ const getc : (f : file# -> status(char))
+
+ /* peeking */
+ const peekb : (f : file# -> status(byte))
+ const peekc : (f : file# -> status(char))
+
+ /* delimited read; returns freshly allocated buffer. */
+ const readln : (f : file# -> status(byte[:]))
+ const readto : (f : file#, delim : byte[:] -> status(byte[:]))
+ const skipto : (f : file#, delim : byte[:] -> bool)
+ const skipspace : (f : file# -> bool)
+
+ /* iterators */
+ const lineiter : (f : file# -> lineiter)
+
+ /* formatted i/o */
+ const put : (f : file#, fmt : byte[:], args : ... -> status(std.size))
+
+ /* unsigned big endian reads */
+ generic getbe8 : (f : file# -> status(@a::(numeric,integral)))
+ generic getbe16 : (f : file# -> status(@a::(numeric,integral)))
+ generic getbe32 : (f : file# -> status(@a::(numeric,integral)))
+ generic getbe64 : (f : file# -> status(@a::(numeric,integral)))
+
+ /* signed big endian reads */
+ generic getle8 : (f : file# -> status(@a::(numeric,integral)))
+ generic getle16 : (f : file# -> status(@a::(numeric,integral)))
+ generic getle32 : (f : file# -> status(@a::(numeric,integral)))
+ generic getle64 : (f : file# -> status(@a::(numeric,integral)))
+
+ /* unsigned big endian */
+ generic putbe8 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putbe16 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putbe32 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putbe64 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+
+ /* unsigned little endian */
+ generic putle8 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putle16 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putle32 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putle64 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ ;;
+
+The Data Structures
+-------------------
+
+ type file = struct
+ fd : std.fd
+ ;;
+
+The `bio.file` type contains the state required to implement buffered files.
+All of the state is internal, except for the file descriptor, which is exposed
+for the purposes of poll() and friends. Reading from it directly may lead to
+inconsistent buffer state, and is strongly not recommended.
+
+ type ioerr = union
+ `Ebadfile
+ `Ebadbuf
+ `Ebadfd
+ `Eioerr
+ ;;
+
+ type status(@a) = union
+ `Eof
+ `Ok @a
+ `Err ioerr
+ ;;
+
+The `bio.status(@a)` union returns the result of the read operation, which
+is going to either be a result of type '@a', an error-free end of file, or
+an error of type `ioerr`. All of these conditions are transient, and are
+retried on subsequent calls to the IO operations. For example, if reading
+a file returns `Eof, but data is appended to a file, then a subsequent read
+will return data again.
+
+In general, errors will be queued up, so if there is a buffered read or
+write that partly succeeds but is interrupted halfway, the error will be
+reported on the next operation. This way, partial operations are not lost.
+
+File Creation and Opening
+-------------------------
+
+ const Rd : mode
+ const Wr : mode
+ const Rw : mode
+
+When opening a file, one of the above flags must be passed to set the mode
+of the file. `Rd` indicates that the bio file should be read only, `Wr`
+indicates that it should be write only, and `Rw` indicates that it should be
+both readable and writable.
+
+Bio file creation
+-------------
+
+ const mkfile : (fd : std.fd, mode : mode -> file#)
+
+This function creates a bio file from a file descriptor and mode, returning a
+`bio.file#` which will buffer reads and/or writes from the fd. This function
+assumes that you are passing it a correctly initialized fd, and will always
+succeed. If the FD is incorrectly configured, uses of it will error out.
+
+Returns: A buffered file wrapping the file descriptor `fd`
+
+ const open : (path : byte[:], mode : mode -> std.result(file#, byte[:]))
+
+This function attempts to open the path passed in with the mode, returning
+a result which is either a file, or a string representing the error that
+prevented the file from being opened.
+
+Returns: A buffered file representing `path` opened with the requested
+permissions.
+
+ const dial : (srv : byte[:], mode : mode -> std.result(file#, byte[:]))
+
+This function is similar to open, however, this function will open a
+connection via a dialstring (as in `std.dial`), and buffer the fd returned
+from that.
+
+Returns: A buffered file representing `dialstr` opened with the requested
+permissions.
+
+ const create : (path : byte[:], mode : mode, perm : int -> std.result(file#, byte[:]))
+
+This function is similar to open, however, this function will attempt to
+atomically create and open the file descriptor.
+
+Returns: A buffered file representing `path` opened with the requested
+permissions.
+
+ const close : (f : file# -> bool)
+
+Closes the file descriptor that the bio file is wrapping, and frees any
+resources used for buffering it. Any data that has not yet been written is
+flushed.
+
+Returns: `true` if flushing the file succeeded, `false` if it failed.
+
+ const free : (f : file# -> void)
+
+Frees any resources used for the file descriptor, but leaves it open. This is
+useful if the file descriptor has been 'stolen' using bio.mkfile, and is not
+owned by the bio file.
+
+ const flush : (f : file# -> bool)
+
+Clears any data that has not been sent to the backing file descriptor.
+
+Returns: `true` if flushing the file succeeded, `false` if it failed.
+
+Binary I/O
+-----------
+
+ const write : (f : file#, src : byte[:] -> result(std.size, byte[:]))
+
+Writes bytes from the buffer `src` to a bio file, returning the number of
+bytes written in the case of success. This number may be smaller than the size
+of the buffer, but will never be larger.
+
+ const read : (f : file#, dst : byte[:] -> result(byte[:]))
+
+Reads from a bio file, into the buffer 'dst', returning the section of the
+buffer that was read in the result.
+
+ const seek : (f : file#, std.off -> std.result(std.off, ioerr))
+
+Seeks the bio file to the given absolute offset.
+
+ const flush : (f : file# -> bool)
+
+Flush attempts to clear the buffers within `f`, writing everything within
+the buffers and discarding them. If writing fails, `false` is returned.
+
+
+Single Unit Operations
+----------------------
+
+ /* single unit operations */
+ const putb : (f : file#, b : byte -> status(std.size))
+
+Writes a single byte out to the file 'f', returning a status. If
+it is successful, the return value represents the number of bytes written.
+For single byte writes, this value is unsurprisingly always 1.
+
+ const putc : (f : file#, c : char -> status(std.size))
+
+Writes a single unicode character out to the file 'f', returning a status.
+This character is encoded in utf-8. If it is successful, the return value
+represents the number of bytes written, varying between 1 and 4.
+
+ const getb : (f : file# -> status(byte))
+
+Reads a single byte from the file `f`, returning a status. If this read
+succeeds, the next byte in the file is returned.
+
+ const getc : (f : file# -> status(char))
+
+Reads a unicode character from the file `f`, returning a status. If this read
+was successful, the next character is returned from the file. The file is
+assumed to be encoded in utf-8.
+
+ /* peeking */
+ const peekb : (f : file# -> status(byte))
+ const peekc : (f : file# -> status(char))
+
+Both peekb and peekc are similar to getb and getc respectively, although
+they return the value without advancing the position within the buffer.
+
+
+Delimited Operations
+--------------------
+
+ /* delimited read; returns freshly allocated buffer. */
+ const readln : (f : file# -> status(byte[:]))
+
+Readln reads a single line of input from the file 'f', returning the line
+with the line ending characters removed. '\n', '\r', and '\r\n' are all
+accepted as valid line endings, and are treated interchangably when reading.
+
+The buffer is heap allocated, and must be freed with std.slfree
+
+ const readto : (f : file#, delim : byte[:] -> status(byte[:]))
+
+Readto is similar to readln, but instead of reading to a line ending, it will
+read up to the point where it finds the requested delimiter. The delimiter is
+an arbitrary sequence of bytes. If an end of file is reached, then the buffer
+up to the Eof is returned.
+
+The buffer is heap allocated, and must be freed with std.slfree
+
+ const skipto : (f : file#, delim : byte[:] -> bool)
+
+Skipto is identical to readto, but instead of allocating a buffer and reading
+into it, skipto will ignore the values and drop them. It returns true for
+any successful reads, and false if an error was encountered.
+
+ const skipspace : (f : file# -> bool)
+
+Skipspace will consume any space tokens from the start of the input stream, It
+returns true for any successful reads, and false if an error was encountered.
+
+Iteration
+---------
+
+ const lineiter : (f : file# -> lineiter)
+
+Lineiter will return an interable type that will iterate through each line
+in a file. These lines are allocated on the heap, and are automatically freed
+at the end of the iteration.
+
+The returned iterator object is a value type, and does not need to be freed.
+
+Formatted IO
+-------------
+
+ const put : (f : file#, fmt : byte[:], args : ... -> status(std.size))
+
+This formats the output using the std.fmt api, and writes it to the file `f`.
+All custom formatters installed for `std.fmt` are used for this formatting.
+This call returns the number of bytes written on success.
+
+Endian Aware Reads
+------------------
+
+ generic getbe8 : (f : file# -> status(@a::(numeric,integral)))
+ generic getbe16 : (f : file# -> status(@a::(numeric,integral)))
+ generic getbe32 : (f : file# -> status(@a::(numeric,integral)))
+ generic getbe64 : (f : file# -> status(@a::(numeric,integral)))
+
+ generic getle8 : (f : file# -> status(@a::(numeric,integral)))
+ generic getle16 : (f : file# -> status(@a::(numeric,integral)))
+ generic getle32 : (f : file# -> status(@a::(numeric,integral)))
+ generic getle64 : (f : file# -> status(@a::(numeric,integral)))
+
+The functions above all read from a bio file, and return the value read on
+success. The 'be' variants read big endian values, and the `le' variants
+read little endian values. They final result is converted to whatever
+numeric, integral type is desired. If the value does not fit within the bounds
+of the type, it is truncated.
+
+The number of bytes read is determined by the number at the end of the
+function call. For eample, `getbe8()` will read 8 bits, or one byte from the
+stream. `getle64` will get a 64 bit, or 8 byte, value. The number of bytes
+consumed is independent of the size of the type being assigned to.
+
+ generic putbe8 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putbe16 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putbe32 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putbe64 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+
+ generic putle8 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putle16 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putle32 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+ generic putle64 : (f : file#, v : @a::(numeric,integral) -> status(std.size))
+
+The functions above all write to a bio file, and return the number of bytes
+written on success. The number of bytes written will always be the size of the
+type. The 'be' variants write the value in a big endian representation, and
+the 'le' variants write it in a little endian representation.
+
+The number of bytes written is determined by the number at the end of the
+function call. For eample, `putbe8()` will write 8 bits, or one byte.
+`putbe64` will write 64 bits, or 8 bytes. The number of
+bytes consumed is independent of the size of the type being assigned to.
+
+Examples
+---------
+
+The example below is the simplest program that creates and opens a file, and
+writes to it. It creates it with 0o644 permissions (ie, rw-r--r--), and then
+immediately closes it. The result of this program should be a file called
+`create-example` containing the words.
+
+ use std
+ use bio
+
+ const main = {
+ var f
+
+ match bio.create("create-example", bio.Wr, 0o644)
+ | `std.Some bio: f = bio
+ | `std.None: std.fatal(1, "Failed to open file\n")
+ ;;
+ bio.write(f, "Hello user\n")
+ bio.close(f)
+ }
+
+The next example shows reading from a file called "lines", line by line. It
+should echo the lines, numbering them as it prints them:
+
+ use std
+ use bio
+
+ const main = {
+ var f
+ var i
+
+ match bio.open("lines", bio.Rd)
+ | `std.Some bio: f = bio
+ | `std.None: std.fatal(1, "Unable to open data file\n")
+ ;;
+
+ while true
+ match bio.readlin(f)
+ | `std.Some ln:
+ std.put("line %i: %s\n", i, ln)
+ | `std.None:
+ break;
+ ;;
+ ;;
+ }
diff --git a/doc/api/libdate/formatting.txt b/doc/api/libdate/formatting.txt
new file mode 100644
index 0000000..c09f2cb
--- /dev/null
+++ b/doc/api/libdate/formatting.txt
@@ -0,0 +1,99 @@
+{
+ title: Formatting
+ description: Libdate API documentation.
+}
+
+Date and Time Formatting
+---------------------
+
+Formatting in libdate is done through the standard formatting
+functionality, and there are actually no functions exposed by libdate.
+Instead, you would write something like:
+
+```{runmyr stdfmt1}
+use date
+
+const main = {
+ std.put("{}\n", date.now())
+}
+```
+
+Custom formatting is done with a format option passed to std.format that
+looks like `f=dateformat`. The format strings used resemble the strings
+provided in strptime. Any characters preceded with a '%' are format
+characters, otherwise they are copied to the output stream directly.
+
+The format strings used to control formatting are also used to control parsing.
+
+An example would look like:
+
+```{runmyr stdfmt1}
+use date
+
+const main = {
+ std.put("{f=year is %Y, the day is %d}\n", date.now())
+}
+
+
+There are a number of short format options installed, specifically, `d`,
+`D`, and `t`, which respectively map to the default date format, the
+default date and time format, and the default time only format.
+
+```{runmyr stdfmt1}
+use date
+
+const main = {
+ std.put("{d}\n", date.now())
+ std.put("{D}\n", date.now())
+ std.put("{T}\n", date.now())
+}
+```
+
+Date and Time Formatting
+---------------------
+
+Both parsing and formatting use the same format strings. The
+modifiers that are supported by libdate are listed below.
+
+When possible, the default format verbs `D`, `d`, or `t`
+should be used for formatting, and the default constants
+`Datefmt`, `Datetimefmt`, or `Timefmt` should be used for
+parsing.
+
+
+Char | Meaning
+------|----------------------------------------------
+_%a_ | Abbreviated day of week: Mon, Tues, etc
+_%A_ | Full day of week: Monday, Tuesday, Wednesday, etc
+_%b_ | Abbreviated month of year: Jan, Feb, Mar, etc.
+_%B_ | Full month of year: January, February, etc
+_%c_ | Short for %Y-%m-%d %H:%M:%S %z (ISO 8601)
+_%C_ | Century number of year, as two digit integer.
+_%d_ | Day of month as a decimal number, 00 to 31.
+_%D_ | Short for "%m/%d/%y (wtf america)"
+_%e_ | Same as d, but space padded instead of zero padded.
+_%F_ | Short for ISO 8601 date format %Y-%m-%d
+_%h_ | Same as '%b'.
+_%H_ | Hour number using the 24 hour clock, zero padded.
+_%I_ | Hour number using the 12 hour clock, zero padded.
+_%j_ | Day of year as a decimal number, 001 to 366.
+_%k_ | Hour number using the 24 hour clock, space padded.
+_%l_ | Hour number using the 12 hour clock, space padded.
+_%m_ | The month number, zero padded.
+_%M_ | The minute number, zero padded.
+_%p_ | AM or PM, according to the time provided.
+_%P_ | am or pm, according to the time provided.
+_%r_ | 12 hour time: %I:%M %p
+_%R_ | 24 hour time: %H:%M
+_%s_ | The number of seconds since the Epoch
+_%S_ | The second number, zero padded. 0 to 60.
+_%T_ | The time including seconds in 24 hour format.
+_%u_ | The day of the week in decimal, 0 to 7.
+_%x_ | The date without the time. Same as %F
+_%X_ | The date with the time. Same as %c
+_%y_ | The final two digits of year.
+_%Y_ | The full year, including century. BC dates are negative.
+_%z_ | Timezone offset.
+_%Z_ | Timezone name or abbreviation. Offset if this is not available.
+_%%_ | A literal '%'
+
diff --git a/doc/api/libdate/index.txt b/doc/api/libdate/index.txt
new file mode 100644
index 0000000..7b9cafa
--- /dev/null
+++ b/doc/api/libdate/index.txt
@@ -0,0 +1,95 @@
+{
+ title: libdate
+ description: Libdate API documentation.
+}
+
+Summary
+-------
+
+Libdate is a date API designed to cover most date related functinality in a
+sane, easy to use way. It will format, parse, and do basic manipulations on
+dates. All operations are done on the proleptic Gregorian calendar, and the
+Julian to transition is not handled.
+
+Core Concepts
+-------------
+
+### Instants
+
+An instant is a point in time. Immovable, unchanging for eternity, it is
+anchored in one spot through the microseconds of unix time in the UTC time
+zone. It is broken up into a local representation, consisting of years,
+months, days, weekdays, hours, minutes, seconds, and microseconds, with a
+timezone attached.
+
+### Durations
+
+A duration is a difference between two instants. It has a fixed magnitude, and
+is independent of timezones and the oddities of human calendars. It may be
+added or subtracted from instants of other durations. Durations have a
+resolution of microseconds. It is signed, and negative durations move instants
+backwards in time.
+
+### Periods
+
+A period is another form of differece between two instants. However, a period
+is a flighty creature, which does not anchor itself to the world of men in any
+strong way. A year may be 365 or 366 days, according to the whims and vagaries
+of the local calendar. An hour added to a time may jump ahead by two hours, if
+it so desires to follow the savings of daylight. These creatures attempt to
+mold themselves to the irrationalities of man's mind, and eschew the divine
+ordering of absolute time handed down by the prophets.
+
+### Timezones
+
+A timezone is a named zone, as decreed by the mighty IANA timezone database.
+It may take the form of a location such as "America/New_York", a well-known
+abbreviation like "EST", or a special value such as "local" or "", which mean,
+respectively, the current zone or UTC.
+
+Conventions
+-----------
+
+Timezones are pervasive, and no time is manipulated in this API without
+the awareness of the timezone. As a result, it is useful to know that dates
+are represeted using IANA zoneinfo database names. These are documented
+fully here: http://www.iana.org/time-zones
+
+There are two extensions that libdate supports: The empty string represents
+the UTC timezone, and "local" represents the time zone of the system that
+the system libdate is running on has been configured to.
+
+In the case of ambiguous timezones -- for example, parsing a date with no
+time attached, while using API call that does not specify the timezone,
+libdate will assume UTC dates.
+
+Functionality
+-------------
+
+The functionality in libdate can be grouped into three main sections: Parsing,
+Manipulation, and Formatting.
+
+### [Types](types)
+
+This covers the set of all types provided by, and used throughout, the API
+of libdate, including all public fields and types.
+
+### [Parsing](parsing)
+
+This covers parse formatted dates in a manner similar to strptime. There are
+currently plans for an flexible parse() function, but this has not yet been
+implemented.
+
+### [Creation and Manipulation](manipulation)
+
+Manipulation covers date and time creation, and transition aware APIs that
+will work for timezone crossings, daylight savings time, and so on.
+
+#### Formatting
+
+This covers date formatting, which is done through strftime-like format
+strings. There are actually no functions exposed for formatting, as this is
+done through custom formatters for libstd, however, the format strings used
+to customize the date output are described here.
+
+
diff --git a/doc/api/libdate/manipulation.txt b/doc/api/libdate/manipulation.txt
new file mode 100644
index 0000000..22f9b9e
--- /dev/null
+++ b/doc/api/libdate/manipulation.txt
@@ -0,0 +1,130 @@
+
+{
+ title: Creation and Manipulation
+ description: Libdate API documentation.
+}
+
+Creation and Manipulation
+-------------------------
+ pkg date =
+ /* useful constructors */
+ const utcnow : (-> instant)
+ const now : (tz : byte[:] -> instant)
+ const tozone : (d : instant, zone : byte[:] -> instant)
+ const mkdate : (y : int, m : int, day : int, zone : byte[:] -> instant)
+ const mkdatetime : (year : int, mon : int, day : int, \
+ h : int, m : int, s : int, zone : byte[:] -> instant)
+ const mkinstant : (tm : std.time, zone : byte[:] -> instant)
+
+ const localoff : (tm : std.time -> duration)
+ const tzoff : (tzname : byte[:], tm : std.time -> duration)
+ const tzname : (tzoff : int -> byte[:])
+ const isleap : (d : instant -> bool)
+
+ /* date differences */
+ const add : (d : instant, dt : duration -> instant)
+ const sub : (d : instant, dt : duration -> instant)
+ const addperiod : (d : instant, dt : period -> instant)
+ const subperiod : (d : instant, dt : period -> instant)
+
+ const duration : (a : instant, b : instant -> duration)
+
+ pkglocal const recalc : (inst : instant# -> std.time)
+ ;;
+
+Creation
+-----------
+
+ const utcnow : (-> instant)
+
+Creates an instant representing the current time. The timezone is UTC. As a
+general philosophical point, dates written out persisetntly should generally
+be in UTC, so this function should generally be used to get dates.
+
+Returns: An instant representing the current time in the UTC timezone.
+
+ const now : (tz : byte[:] -> instant)
+
+Creates an instant representing the current time. The timezone is the local
+time. This is useful for displaying dates and times to the user.
+
+Returns: An instant representing the current time in the local timezone.
+
+ const tozone : (d : instant, zone : byte[:] -> instant)
+
+Takes an instant and converts it to a new timezone. This takes the instants by
+value, and therefore, does not mutate any of its arguments.
+
+Returns: An instant representing provided time in the requested timezone.
+
+ const mkdate : (y : int, m : int, day : int, zone : byte[:] -> instant)
+
+Creates a date with the given year, month, and day in the given timezone. The
+time is set to 0:00:00
+
+Returns: A date representing the given ymd
+
+ const mkdatetime : (year : int, mon : int, day : int, \
+ h : int, m : int, s : int, zone : byte[:] -> instant)
+
+Creates a date and time pair with the given year, month, day, at the time
+h, m, s, in the provided timezone. The microseconds are zeroed.
+
+Returns: A date representing the given ymd:hms
+
+ const mkinstant : (tm : std.time, zone : byte[:] -> instant)
+
+Creates an instant from a time type. This time can be derived from the
+standard library, or computed from any other source. The time is in
+microseconds since the Unix epoch (Jan 1 1970, 0:00, UTC).
+
+Returns: An instant representing a std.time in the requested timezone
+
+Timezones
+---------
+
+ const localoff : (tm : std.time -> duration)
+
+Gets the local timezone offset for a time. Note that timezones can change due
+daylight savings, politics, and other similar factors, so a timezone offset
+needs to be associated with a specific instant.
+
+Returns: a duration representing the local timezone offset.
+
+ const tzoff : (tzname : byte[:], tm : std.time -> duration)
+
+Gets the timezone offset for a time and timezone pair. Note that timezones can
+change due daylight savings, politics, and other similar factors, so a
+timezone offset needs to be associated with a specific instant.
+
+Returns: a duration representing the requested timezone offset.
+
+ const isleap : (d : instant -> bool)
+
+Returns: whether a specific instant is within a leap year or not.
+
+Manipulation
+------------
+
+ const add : (d : instant, dt : duration -> instant)
+ const sub : (d : instant, dt : duration -> instant)
+
+Adds or subtracts a duration from a date. A duration is an absolute length of
+time, and is not adjusted for calendar shifts around DST and similar events.
+
+Returns: an instant representing the adjusted time.
+
+ const addperiod : (d : instant, dt : period -> instant)
+ const subperiod : (d : instant, dt : period -> instant)
+
+Adds or subtracts a period from a date. A period is a humanized length of
+time, and is adjusted for calendar shifts around DST and similar events.
+
+Returns: an instant representing the adjusted time.
+
+ const duration : (a : instant, b : instant -> duration)
+
+Returns: the duration representing the difference between the two provided
+instants.
+
+
diff --git a/doc/api/libdate/parsing.txt b/doc/api/libdate/parsing.txt
new file mode 100644
index 0000000..fc4aa4e
--- /dev/null
+++ b/doc/api/libdate/parsing.txt
@@ -0,0 +1,74 @@
+{
+ title: Date Parsing
+ description: Libdate API documentation.
+}
+
+Date and time parsing
+---------------------
+
+ type parsefail = union
+ `Doublefmt char
+ `Badsep (char, char)
+ `Badfmt char
+ `Badzone byte[:]
+ `Badname byte[:]
+ `Badchar
+ `Badampm
+ `Shortint
+ `Badint
+ ;;
+
+ const Datetimefmt
+ const Datefmt
+ const Timefmt
+
+ /* date i/o */
+ const parsefmt : (fmt : byte[:], s: byte[:] -> std.result(instant, parsefail))
+ const parsefmtl : (fmt : byte[:], s: byte[:] -> std.result(instant, parsefail))
+ const parsefmtz : (fmt : byte[:], s: byte[:], tz : byte[:] -> std.result(instant, parsefail))
+
+Descriptions
+------------
+
+ const Datetimefmt = "%Y-%m-%d %H:%M:%S %z"
+ const Datefmt = "%H:%M:%S %z"
+ const Timefmt = "%Y-%m-%d %z"
+
+These are "sane defaults" for date and time formats, and can be passed in
+where a format is required.
+
+ type parsefail
+
+Parsefail is an error type, returning descriptions of the error that the
+parsing code saw. Strings returned within the error point into the format
+string, and will be invalid when the format string is freed.
+
+ const parsefmt : (fmt : byte[:], s: byte[:] -> std.result(instant, parsefail))
+
+Parses a format string with a format. If there is a timezone specified in the
+format string, that format string will be used. If there is no format, the
+timezone will be assumed to be UTC.
+
+The format string used is similar to strptime, and is documented fully in
+the [description of formatting](/libdate/formatting)
+
+Returns: Either an instant representing the time parsed, or an error
+describing the failure.
+
+ const parsefmtl : (fmt : byte[:], s: byte[:] -> std.result(instant, parsefail))
+
+Parses a format into the local time. If a timezone is specified, the
+conversion will be done from the instant of the timezone to the local time.
+The format strings are the same as 'parsefmt'.
+
+Returns: Either an instant representing the time parsed, or an error
+describing the failure.
+
+ const parsefmtz : (fmt : byte[:], s: byte[:], tz : byte[:] -> std.result(instant, parsefail))
+
+Parses a format into the specified timezone. If a timezone is specified in the
+parsed date, the conversion will be done from the timezone to the provided
+timezone. The format strings are the same as 'parsefmt'.
+
+Returns: Either an instant representing the time parsed, or an error
+describing the failure.
diff --git a/doc/api/libdate/types.txt b/doc/api/libdate/types.txt
new file mode 100644
index 0000000..71c3ebe
--- /dev/null
+++ b/doc/api/libdate/types.txt
@@ -0,0 +1,71 @@
+{
+ title: Types
+ description: Libdate API documentation.
+}
+
+Contents
+--------
+
+ pkg date =
+ type instant = struct
+ actual : std.time /* epoch time in microseconds */
+ tzoff : duration /* timezone offset in microseconds */
+ year : int /* year, != 0 */
+ mon : int /* month, [1..12] */
+ day : int /* day, [1..31] */
+ wday : int /* weekday, [0..6] */
+ h : int /* hour: [0..23] */
+ m : int /* minute: [0..59] */
+ s : int /* second: [0..59] */
+ us : int /* microsecond: [0..999,999] */
+ tzname : byte[:] /* current time zone name */
+ ;;
+
+ type duration = std.time
+
+ type period = union
+ `Year int
+ `Month int
+ `Day int
+ `Hour int
+ `Minute int
+ `Second int
+ ;;
+ ;;
+
+Descriptions
+------------
+
+ type instant
+
+Instant represents a single instant of time, with a resolution
+of microseconds. It contains the actual instant in the member
+`actual`, which is a timestamp in microseconds since Jan 1, 1970
+at 00:00 in UTC, and breaks out the "humanized" time out into the
+various members that are exposed.
+
+The instant type always has a timezone attached, and the humanized
+time components are always in that timezone.
+
+ type duration
+
+A duration is an absolute number of microseconds that can be added
+or subtracted from an instant. This is not timezone adjusted.
+
+ type period
+
+A period is a time delta that is adjusted for crossing timezones,
+daylight savings, and other similar events. If you add a day to
+an instant, you would get the same wallclock time the next day,
+should that wallclock time exist.
+
+For example, if I were to add `\`Day 2` to the instant
+`Oct 31 2015 3:00`, then the result would be the date
+`Nov 2 2015 3:00`, regardless of the daylight savings time adjustment.
+However, adding `\`Hour 48` would not have that effect.
+
+In cases where the adjustment does not exist -- for example, leap years,
+then the time will "wrap around" to the next available day. For example,
+Feb 29th on a leap year will become Mar 1st.
+
+
diff --git a/doc/api/libinifile/index.txt b/doc/api/libinifile/index.txt
new file mode 100644
index 0000000..3952796
--- /dev/null
+++ b/doc/api/libinifile/index.txt
@@ -0,0 +1,135 @@
+{
+ title: libinifile
+ description: Libinifile API documentation.
+}
+
+Summary
+-------
+
+ pkg inifile =
+ type error = union
+ `Fileerr
+ `Parseerr int
+ `Dupkey int
+ ;;
+
+ type inifile =
+ ;;
+
+ /* reading */
+ const load : (path : byte[:] -> std.result(inifile#, error))
+ const loadf : (file : std.fd -> std.result(inifile#, error))
+ const free : (ini : inifile# -> void)
+
+ /* writing */
+ const write : (ini : inifile#, path : byte[:] -> bool)
+
+ /* key getting/setting */
+ const get : (ini : inifile#, sect : byte[:], key : byte[:] -> std.option(byte[:]))
+ const getv : (ini : inifile#, sect : byte[:], key : byte[:], val : byte[:] -> byte[:])
+ const has : (ini : inifile#, sect : byte[:], key : byte[:] -> bool)
+ const put : (ini : inifile#, sect : byte[:], key : byte[:], val : byte[:] -> void)
+ ;;
+
+
+Overview
+--------
+
+Libinifile is a simple ini file parser. It does little interpretation of the
+data, and provides little in the way of convenience features. Loading will
+read the file into memory, and will not reflect changes of the on-disk data.
+
+Functions
+---------
+
+ const load : (path : byte[:] -> std.result(inifile#, error))
+
+Load will read a file from disk, parsing it, and returning either a pointer to
+an `inifile` data structure, or an error reporting the problem parsing it, and
+if applicable, the line that the error occurred on.
+
+This data structure must be freed with `inifile.free()`.
+
+ const loadf : (file : std.fd -> std.result(inifile#, error))
+
+This is identical to `inifile.load`, only it reads from a `std.fd` that has
+already been opened in read mode, instead of a path.
+
+ const free : (ini : inifile# -> void)
+
+Releases all storage associated with an inifile data structure.
+
+ const write : (ini : inifile#, path : byte[:] -> bool)
+
+Write will take the content of an infile, and serialize it to disk. Comments
+from the original ini file are not currently preserved.
+
+ const get : (ini : inifile#, sect : byte[:], key : byte[:] -> std.option(byte[:]))
+ const getv : (ini : inifile#, sect : byte[:], key : byte[:], val : byte[:] -> byte[:])
+
+Get and getv act like `std.hget` and `std.htgetv`. They will retrieve an entry
+from the ini file.
+
+Htget will return `\`std.Some val` if the key is present in the given section,
+or `\`std.None` if there is no value present in the ini file. Htgetv will
+return the default value `val` passed to it if the key is not found.
+
+For a key that is outside of a section, the empty string (`""`) should be
+passed for the section name.
+
+ const has : (ini : inifile#, sect : byte[:], key : byte[:] -> bool)
+
+Queries whether a key is present in the ini file. Returns true if the key is
+present, or false if it is not.
+
+ const put : (ini : inifile#, sect : byte[:], key : byte[:], val : byte[:] -> void)
+
+Places a key value pair into the in-memory representation of the .ini file.
+This key value pair added if it is not present, and replaced if it is. The key
+and value are both copied, and ownership is not taken. The responsibility for
+freeing the previous value lies with the ini file implementation.
+
+Supported Syntax
+--------------
+
+The dialect that it supports allows for a list of zero or more key-value pairs
+before any sections are declared, followed by a list of sections containing
+more key value pairs.
+
+Keys are any sequence of characters, excluding an '=' sign. Values are any
+sequence of characters. For both of these, both leading and trailing white
+space is ignored.
+
+Sections are lists of characters started by `[` and end by `]`. The only
+character disallowed within a section name is `]`. Leading and trailing
+whitespace is stripped from a section.
+
+Keys within a file must be unique, otherwise this is an error.
+
+Section declarations may repeat throughout the file, but this will merely
+switch back into the old section.
+
+Example
+------
+
+Assuming that an file named `demo.ini` exists, and contains the following
+text:
+
+ toplev = hey, there's a value!
+ [section]
+ key = wait, there's another
+
+Then the following program will read it and show the values of the keys:
+
+ use std
+ use inifile
+
+ const main = {
+ var ini
+
+ ini = std.try(inifile.load("demo.ini"))
+ std.put("{}\n", inifile.getv(ini, "", "toplev", "not present")
+ std.put("{}\n", inifile.getv(ini, "section", "key", "not present")
+ inifile.free(ini)
+ }
+
diff --git a/doc/api/libregex/index.txt b/doc/api/libregex/index.txt
new file mode 100644
index 0000000..5cf4716
--- /dev/null
+++ b/doc/api/libregex/index.txt
@@ -0,0 +1,279 @@
+{
+ title: libregex
+ description: Libregex API documentation.
+}
+
+Summary
+-------
+
+ pkg regex =
+ type ast = union
+ /* basic string building */
+ `Alt (ast#, ast#)
+ `Cat (ast#, ast#)
+
+ /* repetition */
+ `Star ast#
+ `Rstar ast#
+ `Plus ast#
+ `Rplus ast#
+ `Quest ast#
+
+ /* end matches */
+ `Chr char
+ `Ranges char[2][:]
+
+ /* meta */
+ `Cap (std.size, ast#) /* id, ast */
+ `Bol /* beginning of line */
+ `Eol /* end of line */
+ `Bow /* beginning of word */
+ `Eow /* end of word */
+ ;;
+
+ type status = union
+ `Noimpl
+ `Incomplete
+ `Unbalanced char
+ `Emptyparen
+ `Badrep char
+ `Badrange byte[:]
+ `Badescape char
+ ;;
+
+ /* regex compilation */
+ const parse : (re : byte[:] -> std.result(ast#, status))
+ const compile : (re : byte[:] -> std.result(regex#, status))
+ const dbgcompile : (re : byte[:] -> std.result(regex#, status))
+ const free : (re : regex# -> void)
+
+ /* regex execution */
+ const exec : (re : regex#, str : byte[:] -> std.option(byte[:][:]))
+ const search : (re : regex#, str : byte[:] -> std.option(byte[:][:]))
+
+ const sub : (re : regex#, str : byte[:], subst : byte[:][:] -> std.option(byte[:]))
+ const sbsub : (sb : std.strbuf#, re : regex#, str : byte[:], subst : byte[:][:] -> bool)
+ const suball : (re : regex#, str : byte[:], subst : byte[:][:] -> byte[:])
+ const sbsuball : (sb : std.strbuf#, re : regex#, str : byte[:], subst : byte[:][:] -> void)
+
+ const matchfree : (pat : byte[:][:] -> void)
+ ;;
+
+
+Overview
+--------
+
+Libregex is a simple regex API that uses a parallel NFA implementation. This
+means that while it is not blazingly fast, it does not exhibit pathological
+behavior on regexes like `(aa|aab?)\*` that many common regex APIs will see.
+
+Regex Syntax
+-------------
+
+The grammar for regexes that are accepted is sketched out below.
+
+ regex : altexpr
+ altexpr : catexpr ('|' altexpr)+
+ catexpr : repexpr (catexpr)+
+ repexpr : baseexpr[*+?][?]
+ baseexpr : literal
+ | charclass
+ | charrange
+ | '.'
+ | '^'
+ | '$'
+ | '(' regex ')'
+ charclass : see below
+ charrange : '[' (literal('-' literal)?)+']'
+
+The following metacharacters have the meanings listed below:
+
+Matches a single unicode character
+
+<table>
+ <tr><tr><th>Metachar</th> <th>Description</th></tr>
+ <tr><td><code>^</td></code> <td>Matches the beginning of a line. Does not consume any characters.</td></tr>
+ <tr><td><code>$</td></code> <td>Matches the end of a line. Does not consume any characters.</td></tr>
+ <tr><td><code>*</td></code> <td>Matches any number of repetitions of the preceding regex fragment.</td></tr>
+ <tr><td><code>+</td></code> <td>Matches one or more repetitions of the preceding regex fragment.</td></tr>
+ <tr><td><code>?</td></code> <td>Matches zero or one of the preceding regex fragment.</td></tr>
+</table>
+
+In order to match a literal metacharacter, it needs to be preceded by a '\' character.
+
+The following character classes are supported:
+
+<table>
+ <tr><tr><th>Charclass</th> <th>Description</th></tr>
+ <tr><td><code>\d </code></td> <td>ASCII digits</td></tr>
+ <tr><td><code>\D </code></td> <td>Negation of ASCII digits</td></tr>
+ <tr><td><code>\x </code></td> <td>ASCII Hex digits</td></tr>
+ <tr><td><code>\X </code></td> <td>Negation of ASCII Hex digits</td></tr>
+ <tr><td><code>\s </code></td> <td>ASCII spaces</td></tr>
+ <tr><td><code>\S </code></td> <td>Negation of ASCII spaces</td></tr>
+ <tr><td><code>\w </code></td> <td>ASCII word characters</td></tr>
+ <tr><td><code>\W </code></td> <td>Negation of ASCII word characters</td></tr>
+ <tr><td><code>\h </code></td> <td>ASCII whitespace characters</td></tr>
+ <tr><td><code>\H </code></td> <td>Negation of ASCII whitespace characters</td></tr>
+ <tr><td><code>\pX</code></td> <td>Characters with unicode property 'X'</td></tr>
+ <tr><td><code>\PX</code></td> <td>Negation of characters with property 'X'</td></tr>
+</table>
+
+The current list of supported Unicode character classes `X` are
+
+<table>
+ <tr><th>Abbrev</th> <th>Full name</th> <th>Description</th></tr>
+ <tr>
+ <td><code>L</code></td> <td><code>Letter</code></td>
+ <td>All letters, including lowercase, uppercase, titlecase,
+ and uncased.</td>
+ </tr>
+ <tr>
+ <td><code>Lu</code></td> <td><code>Uppercase_Letter</code></td>
+ <td>All uppercase letters.</td>
+ </tr>
+ <tr>
+ <td><code>Ll</code></td> <td><code>Lowercase_Letter</code></td>
+ <td>All lowercase letters.</td>
+ </tr>
+ <tr>
+ <td><code>Lt</code></td> <td><code>Titlecase_Letter</code></td>
+ <td>All titlecase letters.</td>
+ </tr>
+ <tr>
+ <td><code>N</code></td> <td><code>Number</code></td>
+ <td>All numbers.</td>
+ </tr>
+ <tr>
+ <td><code>Z</code></td> <td><code>Separator</code></td>
+ <td>All separators, including spaces and control characers.</td>
+ </tr>
+ <tr>
+ <td><code>Zs</code></td> <td><code>Space_Separator</code></td>
+ <td>All space separators, including tabs and ASCII spaces.</td>
+ </tr>
+</table>
+
+Functions
+---------
+
+ const parse : (re : byte[:] -> std.result(ast#, status))
+
+Parse takes a regex string, and converts it to a regex syntax tree, returning
+`\`std.Success ast#` if the regex was valid, or a `\`std.Failure r` if the
+regex could not be parsed. This AST can be used to further process the regex,
+possibly turning it into a multiregex as in Hairless, or using it for NFA and
+DFA tricks.
+
+ const compile : (re : byte[:] -> std.result(regex#, status))
+ const dbgcompile : (re : byte[:] -> std.result(regex#, status))
+
+`compile` takes a regex string, and converts it to a compiled regex, returning
+`\`std.Success regex` if the regex was valid, or a `\`std.Failure r` with the
+reason that the compilation failed. `dbgcompile` is similar, however, the
+regex is compiled so it will spit out a good deal of debugging output. Unless
+you are intent on debugging the internals of the regex engine, this is likely
+only of academic interest.
+
+ const free : (re : regex# -> void)
+
+`free` must be called on a compiled regex to release it's resources after you
+are finished using it.
+
+ const exec : (re : regex#, str : byte[:] -> std.option(byte[:][:])
+
+`exec` runs the regex over the specified text, returning an `\`std.Some matches`
+if the text matched, or `std.None` if the text did not match. matches[0] is
+always the full text that was matched, and will always be returned regardless
+of whether capture groups are specified.
+
+ const search : (re : regex#, str : byte[:] -> std.option(byte[:][:]))
+
+`search` searches for a matching sub-segment of the regex over the specified
+text, returning an `\`std.Some matches` if the text matched, or `std.None` if
+the text did not match. matches[0] is always the full text that was matched,
+and will always be returned regardless of whether capture groups are
+specified. `search` returns the the earliest match in the string provided.
+
+
+ const sub : (re : regex#, str : byte[:], subst : byte[:][:] -> std.option(byte[:]))
+ const sbsub : (sb : std.strbuf#, re : regex#, str : byte[:], subst : byte[:][:] -> bool)
+
+`sub` will take a pattern, an input string, and a set of substitutions, and
+attempt to match. If the match is successful, it will replace each group
+within `str` with `subst`, returning a freshly allocated string. `sbsub`
+behaves identically, however it inserts the new string into the string
+buffer provided, instead of allocating a new string.
+
+If there is no match, then `\`std.None` will be returned.
+
+ const suball : (re : regex#, str : byte[:], subst : byte[:][:] -> byte[:])
+ const sbsuball : (sb : std.strbuf#, re : regex#, str : byte[:], subst : byte[:][:] -> void)
+
+`suball` replaces every match within the string using the given substitutions.
+Only captured groups will be substituted. The remaining text will be left in
+place.
+
+Example
+------
+
+#### Pattern matching
+
+```{runmyr regex}
+use std
+use regex
+
+const main = {
+ match regex.compile("ab(c+)")
+ | `std.Ok re: runwith(re, "abccc")
+ | `std.Fail m: std.fatal("Failed to compile regex\n")
+ ;;
+}
+
+const runwith = {re, txt
+ match regex.exec(re, txt)
+ | `std.Some matches:
+ std.put("matched {}, got {} matches\n", txt, matches.len)
+ for m in matches
+ std.put("Match: {}\n", m)
+ ;;
+ regex.matchfree(matches)
+ | `std.None:
+ std.put("%s did not match\n")
+ ;;
+}
+```
+
+#### Substitution
+
+```{runmyr regex}
+use std
+use regex
+
+const main = {
+ var re
+
+ re = std.try(regex.compile("(a*)bc(d)e"))
+ match regex.sub(re, "aaabcdef", ["HEY", "X"][:])
+ | `std.Some sub:
+ std.put("{}\n", sub[0])
+ regex.matchfree(matches)
+ | `std.None:
+ std.fatal("should have matched")
+ ;;
+}
+```
+
+```{runmyr regex}
+use std
+use regex
+
+const main = {
+ var re, sub
+
+ re = std.try(regex.compile("(b|e)"))
+ sub = regex.suball(re, "aaabbbcdef", ["SUB"][:])
+ std.put("subst: {}\n", sub)
+ std.slfree(sub)
+}
+```
diff --git a/doc/api/libstd/algorithms.txt b/doc/api/libstd/algorithms.txt
new file mode 100644
index 0000000..0787d43
--- /dev/null
+++ b/doc/api/libstd/algorithms.txt
@@ -0,0 +1,129 @@
+{
+ title: Algorithms
+ description: libstd: Algorithms
+}
+
+Algorithms
+----------
+
+ pkg std =
+ /* the result of a comparison */
+ type order = union
+ `Before
+ `Equal
+ `After
+ ;;
+
+ /* sorting and searching */
+ generic sort : (sl:@a[:], cmp:(a:@a, b:@a -> order) -> @a[:])
+ generic lsearch : (sl : @t[:], val : @t, cmp : (a : @t, b : @t -> order) -> option(@idx::(integral,numeric)))
+ generic bsearch : (sl : @t[:], val : @t, cmp : (a : @t, b : @t -> order) -> option(@idx::(integral,numeric)))
+ generic swap : (a : @a#, b : @a# -> void)
+
+ /* prepackaged comparisons */
+ generic numcmp : (a : @a, b : @a -> order)
+ const strcmp : (a : byte[:], b : byte[:] -> order)
+ const strncmp : (a : byte[:], b : byte[:], n : size -> order)
+
+ /* extrema and absolute values */
+ generic min : (a : @a::numeric, b : @a::numeric -> @a::numeric)
+ generic max : (a : @a::numeric, b : @a::numeric -> @a::numeric)
+ generic clamp : (a : @a::numeric, min : @a::numeric, max : @a::numeric -> @a::numeric)
+ generic abs : (a : @a::numeric -> @a::numeric)
+ ;;
+
+Overview
+--------
+
+There are a number of algorithms that are pervasive through many programs.
+These include sorting, searching, and similar
+
+We only cover sorting and searching here, although more would be a good
+addition. Maybe in a separate library.
+
+Types
+-----
+
+ type order = union
+ `Before
+ `Equal
+ `After
+ ;;
+
+When comparing, it's useful to have an ordering between values. The order type
+is the result of a comparison, `a CMP b` describing whether the first value
+`a` comes before, after, or is equivalent to `b`.
+
+Functions: Sorting and Searching
+--------------------------------
+
+ generic sort : (sl:@a[:], cmp:(a:@a, b:@a -> order) -> @a[:])
+
+This function will sort a slice, modifying it in place. The comparison
+function `cmp` is used to decide how to order the slice. This comparison
+function must be transitive -- in otherwords, if A comes before B, and B comes
+before C, then A must come before C. This is true of most comparisons, but
+some care should be taken when attempting to provide "humanized" sorting.
+
+Returns: the same slice it was pased. The slice will not be reallocated or
+moved.
+
+ generic lsearch : (sl : @t[:], val : @t, cmp : (a : @t, b : @t -> order) -> option(@idx::(integral,numeric)))
+
+Performs a linear search for a value using the comparison predicate `cmp`. The
+slice is walked in order until the first value where `cmp` returns `\`Equal`.
+
+Returns: `\`Some idx`, or `\`None` if the value is not present.
+
+ generic bsearch : (sl : @t[:], val : @t, cmp : (a : @t, b : @t -> order) -> option(@idx::(integral,numeric)))
+
+Performs a binary search for a value using the comparison predicate `cmp`. The
+input slice `sl` must be sorted according to the comparsion function `cmp`
+such that for a value at index `idx`, the comparison `cmp(sl[idx - 1],
+sl[idx])` must return either `\`Before` or `\`Equal`.
+
+If there are multiple equal copies value within a list, the index retuned is
+not defined.
+
+Returns: `\`Some idx`, or `\`None` if the value is not present.
+
+ generic swap : (a : @a#, b : @a# -> void)
+
+Takes two pointers to two values, and switches them. If the pointers are
+equal, this is a no-op.
+
+ generic numcmp : (a : @a::numeric, b : @a::numeric -> order)
+ const strcmp : (a : byte[:], b : byte[:] -> order)
+ const strncmp : (a : byte[:], b : byte[:], n : size -> order)
+
+These functions are helpers for comparing values. They will compare any two
+numeric values, and will return the ordering between the two.
+
+Numcmp simply returns the result comparing whether `a` is less than `b`,
+relying on the behavior of the built in operators.
+
+Strcmp and strncmp will do a lexicographical comparison, comparing strings
+byte by byte. This is a useful and correct behavior for both strings of
+arbitrary data, and utf8 encoded strings, where it is equivalent to doing
+a comparison by codepoint.
+
+Functions: Extrema and Clamping
+-------------------------------
+
+ generic min : (a : @a::numeric, b : @a::numeric -> @a::numeric)
+ generic max : (a : @a::numeric, b : @a::numeric -> @a::numeric)
+
+Min and max return the larger or smaller of the two numeric values passed to
+them, respectively. They rely on the built in comparison functions.
+
+ generic clamp : (a : @a::numeric, min : @a::numeric, max : @a::numeric -> @a::numeric)
+
+Clamp clamps the value `a` to the range [min, max], and returns it. This means
+that if `a` is lower than `min`, or greater than `max`, it is adjusted to
+those bounds and returned.
+
+ generic abs : (a : @a::numeric -> @a::numeric)
+
+Abs returns the absolute value of a number. This means that if the number is
+less than 0, it is retuned with the sign inverted. If the type `@a` is
+unsigned, then this function is a no-op.
diff --git a/doc/api/libstd/alloc.txt b/doc/api/libstd/alloc.txt
new file mode 100644
index 0000000..c2692a4
--- /dev/null
+++ b/doc/api/libstd/alloc.txt
@@ -0,0 +1,160 @@
+{
+ title: Allocation
+ description: libstd: Allocation
+}
+
+Memory Allocation
+-----------------
+
+ pkg std =
+ generic mk : (val : @a -> @a#)
+ generic alloc : ( -> @a#)
+ generic zalloc : ( -> @a#)
+ generic free : (v:@a# -> void)
+ generic slalloc : (len : size -> @a[:])
+ generic slzalloc : (len : size -> @a[:])
+ generic slgrow : (sl : @a[:]#, len : size -> @a[:])
+ generic slzgrow : (sl : @a[:]#, len : size -> @a[:])
+ generic slfree : (sl : @a[:] -> void)
+ const bytealloc : (sz:size -> byte#)
+ const zbytealloc : (sz:size -> byte#)
+ const bytefree : (m:byte#, sz:size -> void)
+ ;;
+
+ generic mk : (val : @a -> @a#)
+
+`mk` creates a shallow copy of variable passed to it on the heap, returning a
+pointer to the value that it allocated. It is conventionally used for creating
+new copies of larger complex data structures, although it can be used to
+heapify any value.
+
+Returns: Pointer to fully initialized value of type '@a', based on the value
+passed in.
+
+ generic alloc : ( -> @a#)
+ generic zalloc : ( -> @a#)
+
+`alloc` allocates or free a single element of type @a, respectively. `zalloc`
+does the same, but zeros the memory allocated before returning it. `free` is
+used to return the memory allocated by these functions to the system. In
+general, `mk` is preferred over these functions, as it does not leave any
+values uninitialized.,
+
+ generic slalloc : (len : size -> @a[:])
+ generic slzalloc : (len : size -> @a[:])
+
+`slalloc` allocates or frees a slice of `len` items of type @a. `slzalloc`
+does the same, but zeros the memory allocated before returning it. `slfree`
+is used to return the memory to the system.
+
+ generic slgrow : (sl : @a[:]#, len : size -> @a[:])
+ generic slzgrow : (sl : @a[:]#, len : size -> @a[:])
+
+`slgrow` resizes the slize `sl` to length `len`, allocating the appropriate
+amount of memory. `slzgrow` does the same, but any elements between the old
+and new length are zeroed, as in slzalloc.
+
+ generic free : (v:@a# -> void)
+ generic slfree : (sl : @a[:] -> void)
+
+`free` and `slfree` free the storage allocated allocated for the value or
+slice passed to them , allowing it to be reused again later in the program.
+This memory may be unmapped and returned to the operating system, or it may be
+cached within the program.
+
+Any uses of memory after a `free` call is invalid.
+
+ const bytealloc : (sz:size -> byte#)
+ const zbytealloc : (sz:size -> byte#)
+ const bytefree : (m:byte#, sz:size -> void)
+
+`bytealloc` `bytezalloc`, and `bytefree` are the low level raw-byte allocation
+interface, returning blobs of bytes. Since the only way to use more than one
+of these bytes is to cast to a different type, the generic versions are
+generally a better choice.
+
+Examples
+---------
+
+Overall, the examples here should not be unfamiliar to anyone who has used
+either C or C++.
+
+### Mk
+
+`std.mk` should be used to create new, fully constructed values wherever
+possible. The code for this looks like:
+
+```{runmyr mk-example}
+use std
+
+type mytype =
+ a : int
+ b : char
+ c : byte[:[
+;;
+
+const main = {
+ var v
+
+ v = std.mk([
+ .a = 123
+ .b = 'x'
+ .c = "my string" /* this isn't heapified */
+ ])
+
+ std.free(v)
+}
+```
+
+### Alloc and Zalloc
+
+`alloc` and `zalloc` know the type that they're being assigned to, and use
+this to calulate the size to allocate:
+
+
+```{runmyr mk-example}
+use std
+
+const main = {
+ var x : int#
+ var y : int#
+
+ x = std.alloc()
+ y = std.zalloc()
+ std.free(x)
+ std.free(y)
+}
+```
+
+### Slalloc and Slzalloc
+
+`slalloc` and `slzalloc` take a size to allocate, but infer the type similar
+to `alloc` and `zalloc`. They're freed with std.slfree() and slzfree().
+Thankfully, unlike C++ delete and delete[], it's impossible to pass a slice
+to the wrong free function.
+
+
+```{runmyr mk-example}
+use std
+
+const main = {
+ var x : int[:]
+
+ x = std.slalloc(10) /* slice of 10 ints */
+ std.slfree(x)
+}
+```
+
+Growing slices can be done using slgrow() and slzgrow():
+
+```{runmyr mk-example}
+use std
+
+const main = {
+ var x : int[:]
+
+ x = std.slalloc(10) /* slice of 10 ints */
+ x = std.slzgrow(x, 20) /* x[10:20] are guaranteed to be zeroed.*/
+ std.slfree(x)
+}
+```
diff --git a/doc/api/libstd/bigint.txt b/doc/api/libstd/bigint.txt
new file mode 100644
index 0000000..dfcc180
--- /dev/null
+++ b/doc/api/libstd/bigint.txt
@@ -0,0 +1,208 @@
+{
+ title: Bigints
+ description: libstd: Bigints
+}
+
+Bigints
+-------
+
+ pkg std =
+ type bigint = struct
+ ;;
+
+ generic mkbigint : (v : @a::(numeric,integral) -> bigint#)
+ const bigfree : (a : bigint# -> void)
+ const bigdup : (a : bigint# -> bigint#)
+ const bigassign : (d : bigint#, s : bigint# -> bigint#)
+ const bigmove : (d : bigint#, s : bigint# -> bigint#)
+ const bigparse : (s : byte[:] -> option(bigint#))
+ const bigclear : (a : bigint# -> bigint#)
+ const bigbfmt : (b : byte[:], a : bigint#, base : int -> size)
+ const bigtoint : (a : bigint# -> @a::(numeric,integral))
+ const bigiszero : (a : bigint# -> bool)
+ const bigeq : (a : bigint#, b : bigint# -> bool)
+ const bigcmp : (a : bigint#, b : bigint# -> order)
+ const bigadd : (a : bigint#, b : bigint# -> bigint#)
+ const bigsub : (a : bigint#, b : bigint# -> bigint#)
+ const bigmul : (a : bigint#, b : bigint# -> bigint#)
+ const bigdiv : (a : bigint#, b : bigint# -> bigint#)
+ const bigmod : (a : bigint#, b : bigint# -> bigint#)
+ const bigdivmod : (a : bigint#, b : bigint# -> (bigint#, bigint#))
+ const bigshl : (a : bigint#, b : bigint# -> bigint#)
+ const bigshr : (a : bigint#, b : bigint# -> bigint#)
+ const bigmodpow : (b : bigint#, e : bigint#, m : bigint# -> bigint#)
+ const bigpow : (a : bigint#, b : bigint# -> bigint#)
+ generic bigeqi : (a : bigint#, b : @a::(numeric,integral) -> bool)
+ generic bigaddi : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ generic bigsubi : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ generic bigmuli : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ generic bigdivi : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ generic bigshli : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ generic bigshri : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ const bigpowi : (a : bigint#, b : uint64 -> bigint#)
+ ;;
+
+
+Overview
+--------
+
+While bigint usage in most programs is relatively rare, libstd needs them
+internally for handling floats, and several other widely used pieces of
+functionality also need them.
+
+This set of code covers the bulk of common big integer operations: Creation,
+input, output, and various arithmetic operations
+
+By convention, with a few exceptions, all operations on bigintegers will
+modify the bigint in place, and return a pointer to the first argument
+of the function. This allows for easy chaining of operations.
+
+A formatter for bigintegers is installed by default, so `std.put("{}\n",
+mybig)` will show a reasonably formatted big integer. The standard `x`
+modifier is recognized to print the value in hex.
+
+Types
+-----
+
+ type bigint = struct
+ ;;
+
+This is a big integer. It stores big integers. Like it was an integer. But
+bigger.
+
+
+
+Functions: Bookkeeping and IO
+-----------------------
+
+ generic mkbigint : (v : @a::(numeric,integral) -> bigint#)
+
+Mkbigint takes a regular small int, and creates a biginteger from that
+value. It allocates the value on the heap, returning a pointer to the bigint
+instance. This instance must be freed with `bigfree`.
+
+ const bigfree : (a : bigint# -> void)
+
+Cleans up the storage associated with the bigint `a`.
+
+ const bigdup : (a : bigint# -> bigint#)
+
+Bigdup creates a new biginteger, and copies the value from the original
+biginteger `a` into it. It returns a new biginteger.
+
+ const bigassign : (d : bigint#, s : bigint# -> bigint#)
+
+Bigassign copies the value of the bigint `s` to `d`, and returns
+a pointer to `d`. No allocations or new values are created.
+
+ const bigmove : (d : bigint#, s : bigint# -> bigint#)
+
+Bigmove clears the value of `d`, and moves the value from `s` into
+it efficiently, tearing down the value of `s` in the process. It
+returns a pointer to `d`.
+
+For example, if you had the `a=123` and `b=246`, then moving `a <= b`,
+the final values would be `a = 246` and `b = 0`.
+
+No new values are allocated.
+
+ const bigparse : (s : byte[:] -> option(bigint#))
+
+Bigparse converts a string representation of an integer into a bigint,
+returning `\`Some val` if the string is a valid bigint, or `\`None` otherwise.
+
+Decimal, hex, octal, and binary biginteger strings are recognized. Decimal
+is the default, and is recognized unprefixed. Hex must be prefixed with `0x`,
+octal must be prefixed with `0o`, and binary must be prefixed with `0b`. As
+with all Myrddin integers, '_' is accepted and ignored within numerical
+constants.
+
+ const bigclear : (a : bigint# -> bigint#)
+
+Bigclear zeroes out a biginteger, returning it.
+
+ const bigtoint : (a : bigint# -> @a::(numeric,integral))
+
+
+Bigtoint returns the low order digits of a bigint as an integer value. Care
+must be taken when using this function to ensure that values are not
+undesirably truncated.
+
+Functions: Arithmetic
+---------------------
+
+ const bigiszero : (a : bigint# -> bool)
+
+Bigiszero checks if a bigint is zero, and returns true if it is, or false if
+it is not.
+
+ const bigeq : (a : bigint#, b : bigint# -> bool)
+
+Bigeq compares whether two integers are equal.
+
+ const bigcmp : (a : bigint#, b : bigint# -> order)
+
+Bigcmp compares two big integers, and returns the ordering between them.
+`\`Before` if a < b, `\`After` if a > b, and `\`Equal` if the two values
+are equal.
+
+ const bigadd : (a : bigint#, b : bigint# -> bigint#)
+ const bigsub : (a : bigint#, b : bigint# -> bigint#)
+ const bigmul : (a : bigint#, b : bigint# -> bigint#)
+ const bigdiv : (a : bigint#, b : bigint# -> bigint#)
+ const bigmod : (a : bigint#, b : bigint# -> bigint#)
+ const bigshl : (a : bigint#, b : bigint# -> bigint#)
+ const bigshr : (a : bigint#, b : bigint# -> bigint#)
+ const bigmodpow : (b : bigint#, e : bigint#, m : bigint# -> bigint#)
+ const bigpow : (a : bigint#, b : bigint# -> bigint#)
+
+All of these functions follow the convention mentioned in the summary: They
+apply the operation `a = a OP b`, where `a` is the first argument, and `b`
+is the second argument. They return `a`, without allocating a new result.
+
+ const bigdivmod : (a : bigint#, b : bigint# -> (bigint#, bigint#))
+
+Bigdivmod is an exception to the above convention. Because it needs to return
+two values, it returns a tuple of newly allocated bigints.
+
+ generic bigeqi : (a : bigint#, b : @a::(numeric,integral) -> bool)
+ generic bigaddi : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ generic bigsubi : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ generic bigmuli : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ generic bigdivi : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ generic bigshli : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ generic bigshri : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ const bigpowi : (a : bigint#, b : uint64 -> bigint#)
+
+All of these are identical ot the bigint operations above, however instead of
+taking a bigint for their second operand, they take an integer. This is useful
+for when operations like multiplication or division by a small constant is
+needed.
+
+Examples
+--------
+
+```{runmyr bigcmp}
+
+use std
+use bio
+
+const main = {args : byte[:][:]
+ var f
+
+ a = try(std.bigparse("1234_5678_1234_6789_6666_7777_8888"))
+ b = try(std.bigparse("0x75f3_fffc_1123_5ce4"))
+
+ match std.bigcmp(a, b)
+ | `std.Equal: "{} is equal to {}\n", a, b)
+ | `std.Before: "{} is less than {}\n", a, b)
+ | `std.After: "{} is greater than {}\n", a, b)
+ ;;
+
+ std.bigmul(a, b)
+ std.bigdivi(a, 42)
+ std.put("a * b / 42 = {}\n", a)
+
+ std.bigfree(a, b)
+}
+```
diff --git a/doc/api/libstd/cli.txt b/doc/api/libstd/cli.txt
new file mode 100644
index 0000000..b850a2a
--- /dev/null
+++ b/doc/api/libstd/cli.txt
@@ -0,0 +1,290 @@
+{
+ title: CLI Parsing
+ description: libstd: CLI Parsing
+}
+
+
+Command Line Parsing
+--------------------
+
+Command line parsing is something that nearly every program needs. This
+section of libstd provides simple command line parsing with autogenerated
+help.
+
+ pkg std =
+ type optdef = struct
+ argdesc : byte[:] /* the description for the usage */
+ minargs : std.size /* the minimum number of positional args */
+ maxargs : std.size /* the maximum number of positional args (0 = unlimited) */
+ noargs : std.bool /* whether we accept args at all */
+ opts : optdesc[:] /* the description of the options */
+ ;;
+ type optdesc = struct
+ opt : char
+ arg : byte[:]
+ desc : byte[:]
+ optional : bool
+ ;;
+ type optparsed = struct
+ opts : (char, byte[:])[:]
+ args : byte[:][:]
+ ;;
+
+ const optparse : (optargs : byte[:][:], def : optdef# -> optparsed)
+ const optusage : (prog : byte[:], def : optdef# -> void)
+ ;;
+
+Syntax
+-------
+
+A command line is composed of a list of words, known as. These arguments may
+be options that the program can act on, known as "flags". These flags may take
+up to one argument. To avoid confusing with the top level arguments, this
+document will refer to them as "values". Anything that is not a flag is a
+"positional argument", or simply an "argument".
+
+In general, the POSIX syntax for arguments is followed, with a few minor
+enhancements. Myrddin program will use the following semantics for command
+line options:
+
+ - Arguments are groupls of flags if they follow a '-'. A flag is any
+ single unicode character, potentially followed by a single value. This
+ value may be optional.
+ - Flags that take values will consume the remainder of the argument as
+ the value. If the remainder of the argument is empty, then the next
+ argument is consumed as the value. For example, `-foo` and `-f oo`
+ are equivalent.
+ - Any flags that do not take arguments may be placed before other
+ flags within the same argument. For example, `-x -y -z` is equivalent
+ to `-xyz`, as long as `-x` and `-y` have no optional arguments.
+ - The first '--' stops flags from being recognized, and treats them
+ as arguments.
+ - Flags may be supplied in any order, intermingled with arguments,
+ and repeated as many times as desired. An unrecognized flag is an
+ error, and will trigger a usage message.
+
+Types
+------
+
+The API provided for command line parsing is relatively declarative, with the
+options specified in a struct passed to the parsing.
+
+ type optdef = struct
+ argdesc : byte[:]
+ minargs : std.size
+ maxargs : std.size
+ noargs : std.bool
+ opts : optdesc[:]
+ ;;
+
+The optdef is the top level structure describing the command line arguments.
+It contains the following fields:
+
+<dl>
+ <dt><code>argdesc</code></dt>
+ <dd>
+ <p>Argdesc is a string describing the positional arguments passed to the
+ program. It doesn't change the way that the arguments are parsed, but is
+ used to document the arguments to the user.</p>
+
+ <p>In general, this should be a summary of any expected argument. If a
+ variable number of them are expected, the argument should be followed
+ with a <code>...</code>.</p>
+
+ <p>For example, a program that takes an output and a list of inputs may
+ provide the following for <code>argdesc</code>:</p>
+
+ <p><code>"output inputs..."</code></p>
+
+ <p>When the help string is generated, the output would look like:</p>
+ <p><code>myprog [-o option] output inputs...</code></p>
+ </dd>
+
+ <dt><code>minargs</code></dt>
+ <dd>
+ <p>This argument limits the minimum number of arguments that the
+ program will accept without error. If at minimum 3 inputs are needed, for
+ example, then this value should be set to 3. This does not count flags,
+ nor does it count the program name.</p>
+ <p> If set to 0, this value is ignored. This is the default value.</p>
+ </dd>
+
+ <dt><code>maxargs</code></dt>
+ <dd>
+ <p>This argument limits the maximum number of arguments that the program
+ will accept without error. If the program takes at most 1 argument, for
+ example, example, then this value should be set to 3. Just like
+ <code>maxargs</code>, this does not count flags or the program name.
+ </p>
+ <p> If set to 0, this value is ignored. This is the default value.</p>
+ </dd>
+
+ <dt><code>noargs</code></dt>
+ <dd>
+ <p>This argument causes the program to reject any arguments at all.</p>
+ </dd>
+ <dt><code>opts</code></dt>
+ <dd><p>This is a list of descriptions of the options that this program
+ takes. This list may be empty, at which point this api still provides a
+ good way of checking that no invalid arguments are passed.</p>
+ </dd>
+</dl>
+
+
+ type optdesc = struct
+ opt : char
+ arg : byte[:]
+ desc : byte[:]
+ optional : bool
+ ;;
+
+This is a description of a command line argument. It contains the following
+fields to be set by the user:
+
+<dl>
+ <dt><code>opt</code></dt>
+ <dd>
+ <p>This is a single unicode character that is used for the option
+ flag.</p>
+ </dd>
+ <dt><code>arg</code></dt>
+ <dd>
+ <p>This is a single word description of the argument. If it is not present
+ or has zero length, this indicates that the flag takes no value.
+ Otherwise, the value is mandatory, unless the <code>optional</code> flag
+ is set.</p>
+ </dd>
+ <dt><code>optional</code></dt>
+ <dd>
+ <p>This is a boolean that allows for the value <code>arg</code> to be
+ optionally omitted when using the flag. It is disabled by default.
+ </p>
+ </dd>
+ <dt><code>desc</code></dt>
+ <dd>
+ <p>This is a short sentence describing <code>arg</code>. It has no
+ semantic effect on the option parsing, and is only used in generating
+ help output for the arguments.
+ </p>
+ </dd>
+</dl>
+
+
+ type optparsed = struct
+ opts : (char, byte[:])[:]
+ args : byte[:][:]
+ prog : byte[:]
+ ;;
+
+This is the final result of parsing the options. The `opts` member contains a
+list of options in the form of `(opt, val)` pairs. The option `opt` will be
+repeated once for every time that the flag `opt` is seen within the command
+line.
+
+If there is no value passed with the flag, then the string will be the empty
+string. Otherwise, it will contain the string passed.
+
+The `args` member contains the arguments, collected for easy iteration, and the
+`prog` member contains the binary name.
+
+Functions
+----------
+
+ const optparse : (optargs : byte[:][:], def : optdef# -> optparsed)
+
+Optparse takes an array `optargs` containing the command line arguments passed
+to the program, as well as an `optdef` pointer describing the expected
+arguments, and spits out out an `optparsed`. The arguments `optargs` are
+expected to contain the program name.
+
+ const optusage : (prog : byte[:], def : optdef# -> void)
+
+
+Optusage takes the string `prog` containing the program name, and an `def`
+containing an `optdef` which describes the arguments to provide help for. It
+prints these out on `stderr` (fd 1), and returns.
+
+
+Examples:
+--------
+
+This example is a trivial one, which parses no flags, and merely
+errors if given any.
+
+ const main = {args
+ var cmd
+
+ cmd = std.optparse(args, &[
+ .argdesc = "vals",
+ ])
+ for arg in cmd.args
+ std.put("arg: {}\n", arg)
+ ;;
+ }
+
+This example shows some more advanced usage, and is extracted from
+mbld.
+
+ const main = {args
+ var dumponly
+ var targname
+ var bintarg
+ var cmd
+ var libpath
+
+ cmd = std.optparse(args, &[
+ .argdesc = "[inputs...]",
+ .opts = [
+ [.opt='t', .desc="list all available targets"],
+ [.opt='T', .arg="tag", .desc="build with specified systag"],
+ [.opt='S', .desc="generate assembly when building"],
+ [.opt='d', .desc="dump debugging information for mbld"],
+ [.opt='I', .arg="inc", .desc="add 'inc' to your include path"],
+ [.opt='R', .arg="root", .desc="install into 'root'"],
+ [.opt='b', .arg="bin", .desc="compile binary named 'bin' from inputs"],
+ [.opt='l', .arg="lib", .desc="compile lib named 'lib' from inputs"],
+ [.opt='r', .arg="rt", .desc="link against runtime 'rt' instead of default"],
+ [.opt='C', .arg="mc", .desc="compile with 'mc' instead of the default compiler"],
+ [.opt='M', .arg="mu", .desc="merge uses with 'mu' instead of the default muse"],
+ ][:]
+ ])
+ targname = ""
+ tags = [][:]
+ for opt in cmd.opts
+ match opt
+ | ('t', ""): dumponly = true
+ | ('S', ""): bld.opt_genasm = true
+ | ('I', arg): bld.opt_incpaths = std.slpush(bld.opt_incpaths, arg)
+ | ('R', arg): bld.opt_instroot = arg
+ | ('T', tag): tags = std.slpush(tags, tag)
+ | ('b', arg):
+ targname = arg
+ bintarg = true
+ | ('l', arg):
+ targname = arg
+ bintarg = false
+ | ('r', arg):
+ if std.sleq(arg, "none")
+ bld.opt_runtime = ""
+ else
+ bld.opt_runtime = arg
+ ;;
+ /*
+ internal undocumented args; used by compiler suite for
+ building with an uninstalled compiler.
+ */
+ | ('d', arg): bld.opt_debug = true
+ | ('C', arg): bld.opt_mc = arg
+ | ('M', arg): bld.opt_muse = arg
+ | _: std.die("unreachable\n")
+
+ ;;
+ ;;
+
+ for arg in cmd.args
+ /* build stuff */
+ ;;
+ }
+
+
+
diff --git a/doc/api/libstd/datastruct.txt b/doc/api/libstd/datastruct.txt
new file mode 100644
index 0000000..651cd0a
--- /dev/null
+++ b/doc/api/libstd/datastruct.txt
@@ -0,0 +1,181 @@
+{
+ title: Data Structures
+ description: libstd: Data Structures
+}
+
+
+Data Structures
+----------------
+
+ pkg std =
+ type htab(@k, @v) = struct
+ ;;
+
+ type bitset = struct
+ ;;
+
+ /* hash tables */
+ generic mkht : (h : (k : @k -> uint32), eq : (a : @k, b : @k -> bool) -> htab(@k, @v)#)
+ generic htfree : (ht : htab(@k, @v)# -> void)
+ generic htput : (ht : htab(@k, @v)#, k : @k, v : @v -> void)
+ generic htdel : (ht : htab(@k, @v)#, k : @k -> void)
+ generic htget : (ht : htab(@k, @v)#, k : @k -> option(@v))
+ generic htgetv : (ht : htab(@k, @v)#, k : @k, fallback : @v-> @v)
+ generic hthas : (ht : htab(@k, @v)#, k : @k -> bool)
+ generic htkeys : (ht : htab(@k, @v)# -> @k[:])
+
+ /* bit sets */
+ const mkbs : (-> bitset#)
+ const bsdup : (bs : bitset# -> bitset#)
+ const bsfree : (bs : bitset# -> void)
+ const bsmax : (a : bitset# -> size)
+ generic bsput : (bs : bitset#, v : @a::(integral,numeric) -> bool)
+ generic bsdel : (bs : bitset#, v : @a::(integral,numeric) -> bool)
+ generic bshas : (bs : bitset#, v : @a::(integral,numeric) -> bool)
+ const bsdiff : (a : bitset#, b : bitset# -> void)
+ const bsintersect : (a : bitset#, b : bitset# -> void)
+ const bsunion : (a : bitset#, b : bitset# -> void)
+ const bseq : (a : bitset#, b : bitset# -> bool)
+ const bsissubset : (a : bitset#, b : bitset# -> bool)
+ const bsclear : (bs : bitset# -> bitset#)
+
+ /* prepackaged hashing and equality tests */
+ const strhash : (s : byte[:] -> uint32)
+ const streq : (a : byte[:], b : byte[:] -> bool)
+ generic ptrhash : (p : @a# -> uint32)
+ generic ptreq : (a : @a#, b : @a# -> bool)
+ generic inthash : (v : @a::(integral,numeric) -> uint32)
+ generic inteq : (a : @a::(integral,numeric), b : @a::(integral,numeric) -> bool)
+ generic slhash : (sl : @a[:] -> uint32)
+ ;;
+
+Hash Tables
+-----------
+
+The need for key value lookup shows up everywhere, so libstd contains an
+implementation of hash tables.
+
+ type htab(@k, @v) = struct
+ ;;
+
+The hash table is a generic type which contains any key and any value. The
+key used is `@k`, and the value is `@v`.
+
+ generic mkht : (h : (k : @k -> uint32), eq : (a : @k, b : @k -> bool) -> htab(@k, @v)#)
+
+Mkht creates a hash table on the heap. It accepts two functions, for hashing
+and equality comparison. The hash table should be freed with `htfree`.
+
+ generic htfree : (ht : htab(@k, @v)# -> void)
+
+Htfree frees a hash table and associated storage. The keys and values remain
+untouched.
+
+ generic htput : (ht : htab(@k, @v)#, k : @k, v : @v -> void)
+
+Inserts a key value pair into the hash table `ht`. If there is already a value
+with the key `k`, then the key value pair will be replaced.
+
+ generic htdel : (ht : htab(@k, @v)#, k : @k -> void)
+
+Removes a key value pair from the hash table `ht`.
+
+ generic htget : (ht : htab(@k, @v)#, k : @k -> option(@v))
+
+Looks up a value from a hash table, returning `\`Some v` if the key is
+present, or `\`None` if the value is not present.
+
+ generic htgetv : (ht : htab(@k, @v)#, k : @k, fallback : @v-> @v)
+
+Looks up a value from a hash table, returning the value if the key is
+present, or `fallback` if it is not present.
+
+ generic hthas : (ht : htab(@k, @v)#, k : @k -> bool)
+
+Looks up a value from a hash table, returning `true` if the key is
+present, or `falase` if the value is not present.
+
+ generic htkeys : (ht : htab(@k, @v)# -> @k[:])
+
+Returns a list of all the keys present in the hash table. This list is
+heap allocated, and must be freed with `slfree`.
+
+
+Bit Sets
+--------
+
+The need for sets lookup shows up in many places, so libstd contains an
+implementation of bit sets. Any numeric value can be put into the set,
+and with the current API they may be freely intermixed [BUG?]
+
+ type bitset = struct
+ ;;
+
+The bitset holds a set of integers. It works well for relatively dense, small
+integers, as storage used is `O(max_value)`.
+
+ const mkbs : (-> bitset#)
+
+Creates an empty bit set. The returned bit set should be freed with `bsfree`.
+
+ const bsdup : (bs : bitset# -> bitset#)
+
+Duplicates an existing bit set. The returned bit set should be freed with
+`bsfree`.
+
+ const bsfree : (bs : bitset# -> void)
+
+Frees all resources associated with the bitset `bs`.
+
+ const bsmax : (a : bitset# -> size)
+
+Returns the maximum value that the bitset contains. This is an approximation
+of the capacity of the bitset, not a hard limit on the number of elements.
+
+ const bscount : (a : bitset# -> size)
+
+Returns the total number of elements that the bitset contains. This is an
+O(n) operation that involves iterating all of the bits.
+
+ generic bsput : (bs : bitset#, v : @a::(integral,numeric) -> bool)
+
+Inserts the integer value `v` into the bit set `bs`. Returns `true` if this
+operation changed the set, or `false` otherwise.
+
+ generic bsdel : (bs : bitset#, v : @a::(integral,numeric) -> bool)
+
+Removes the integer value `v` from the bit set `bs`. Returns `true` if this
+operation changed the set, or `false` otherwise.
+
+ generic bshas : (bs : bitset#, v : @a::(integral,numeric) -> bool)
+
+Returns whether the bit set `bs` contains the value `v`.
+
+ const bsdiff : (a : bitset#, b : bitset# -> void)
+
+Takes the set difference between `a` and `b`, storing the result back into
+`a`.
+
+ const bsintersect : (a : bitset#, b : bitset# -> void)
+
+Takes the set intersection between `a` and `b`, storing the result back into
+`a`.
+
+ const bsunion : (a : bitset#, b : bitset# -> void)
+
+Takes the set union between `a` and `b`, storing the result back into `a`.
+
+ const bseq : (a : bitset#, b : bitset# -> bool)
+
+Tests whether the bitsets `a` and `b` contain the same elements, returning
+`true` if they are equivalent and `false` otherwise.
+
+ const bsissubset : (a : bitset#, b : bitset# -> bool)
+
+Tests whether every element of `b` is also within `a`, returning `true` if
+`true` if `b` is a subset of `a`, and `false` otherwise.
+
+ const bsclear : (bs : bitset# -> bitset#)
+
+Zeros every value within the bitset `bs`. This is equivalent to iterating
+through it and deleting every element.
diff --git a/doc/api/libstd/dns.txt b/doc/api/libstd/dns.txt
new file mode 100644
index 0000000..86cf5f4
--- /dev/null
+++ b/doc/api/libstd/dns.txt
@@ -0,0 +1,102 @@
+{
+ title: DNS
+ description: libstd: DNS
+}
+
+Networking
+----------
+
+ pkg std =
+ type rectype = union
+ `DnsA /* host address */
+ `DnsNS /* authoritative name server */
+ `DnsCNAME /* canonical name for an alias */
+ `DnsSOA /* marks the start of a zone of authority */
+ `DnsWKS /* well known service description */
+ `DnsPTR /* domain name pointer */
+ `DnsHINFO /* host information */
+ `DnsMINFO /* mailbox or mail list information */
+ `DnsMX /* mail exchange */
+ `DnsTXT /* text strings */
+ `DnsAAAA /* ipv6 host address */
+ ;;
+
+ type resolveerr = union
+ `Badhost
+ `Badsrv
+ `Badquery
+ `Badresp
+ ;;
+
+ type hostinfo = struct
+ fam : sys.sockfam
+ stype : sys.socktype
+ ttl : uint32
+ addr : netaddr
+ ;;
+
+ const resolve : (host : byte[:] -> result(hostinfo[:], resolveerr))
+ const resolvemx : (host : byte[:] -> result(hostinfo[:], resolveerr))
+ const resolverec : (host : byte[:], t : rectype -> result(hostinfo[:], resolveerr))
+ ;;
+
+
+Data Types
+----------
+
+ type rectype = union
+ `DnsA /* host address */
+ `DnsNS /* authoritative name server */
+ `DnsCNAME /* canonical name for an alias */
+ `DnsSOA /* marks the start of a zone of authority */
+ `DnsWKS /* well known service description */
+ `DnsPTR /* domain name pointer */
+ `DnsHINFO /* host information */
+ `DnsMINFO /* mailbox or mail list information */
+ `DnsMX /* mail exchange */
+ `DnsTXT /* text strings */
+ `DnsAAAA /* ipv6 host address */
+ ;;
+
+This union contains all of the record types that we claim to know how to
+resolve. At the moment, few of them have been tested sufficiently (only A
+records can reasonably be said to be exercised).
+
+ type resolveerr = union
+ `Badhost
+ `Badsrv
+ `Badquery
+ `Badresp
+ ;;
+
+This union contains the errors that we can encounter when trying to resolve a
+host.
+
+ type hostinfo = struct
+ fam : sys.sockfam
+ stype : sys.socktype
+ ttl : uint32
+ addr : netaddr
+ ;;
+
+DNS Resolution
+--------------
+
+ const resolve : (host : byte[:] -> result(hostinfo[:], resolveerr))
+
+Resolves the A or AAAA record for the host `host` using DNS. This function
+does caching, expiring based on the TTL. Returns all of the host info entries sent
+back by DNS or found in the cache on success, or a resolve error on failure.
+
+ const resolvemx : (host : byte[:] -> result(hostinfo[:], resolveerr))
+
+Resolves the MX record for the host `host` using DNS. This function does
+caching, expiring based on the TTL. Returns all of the host info entries sent
+back by DNS or found in the cache on success, or a resolve error on failure.
+
+ const resolverec : (host : byte[:], t : rectype -> result(hostinfo[:], resolveerr))
+
+Resolves a record from DNS. This function's interface is slightly broken, as
+it will never work for TXT or CNAME records.
+
+
diff --git a/doc/api/libstd/err.txt b/doc/api/libstd/err.txt
new file mode 100644
index 0000000..53904e7
--- /dev/null
+++ b/doc/api/libstd/err.txt
@@ -0,0 +1,116 @@
+{
+ title: Error Handling
+ description: libstd: Error Handling
+}
+
+
+Error Handling
+--------------
+
+ pkg std =
+ type option(@a) = union
+ `Some @a
+ `None
+ ;;
+
+ type result(@a, @b) = union
+ `Ok @a
+ `Fail @b
+ ;;
+
+ $noret const fatalv : (fmt : byte[:], ap : valist# -> void)
+ $noret const fatal : (fmt : byte[:], args : ... -> void)
+ const assert : (cond : bool, fmt : byte[:], args : ... -> void)
+ const die : (msg : byte[:] -> void)
+ const suicide : ( -> void)
+
+ generic try : (v : result(@a, @b) -> @a)
+ generic tryv : (v : result(@a, @b), d : @a -> @a)
+ generic get : (v : option(@a) -> @a)
+ generic getv : (v : option(@a), d : @a -> @a)
+ ;;
+
+Overview
+--------
+
+Myrddin does not have exceptions. By convention, code will abort on
+programmer errors, such as passing invalid values where valid ones
+were expected -- for example, calling `std.fmt("{}")` with the wrong
+number of arguments int the list.
+
+For recoverable error conditions that depend on the environment, and
+not the developer making a mistake, one of the branched return types
+are conventionally used.
+
+For conditions where something can be either present or not, the `option(@a)`
+type is used. For places where there can be either a result or an error, the
+`result(@a, @e)` type is used.
+
+Generally, by convention, the type returned for the result should have a
+custom that converts it to something directly displayable to the user.
+
+Types
+-----
+
+ type option(@a) = union
+ `Some @a
+ `None
+ ;;
+
+As mentioned in the overview, `option(@a)` is a type that wraps up a result
+and error type. It is typically used in places where a missing value is the
+only exceptional condition.
+
+ type result(@a, @b) = union
+ `Ok @a
+ `Fail @b
+ ;;
+
+
+The type `result(@a, @e)` is used to signal either success or an error
+condition. The first type parameter, `@a` is what is returned on success,
+and the second, `@b` is returned on failure.
+
+
+Functions
+---------
+ $noret const fatalv : (fmt : byte[:], ap : valist# -> void)
+ $noret const fatal : (fmt : byte[:], args : ... -> void)
+
+Both fatal and fatalv exit the program with an error message, formatted as
+in `std.fmt`. They do not return.
+
+They exit with a failure status. On Unix, this status is `1`. On Plan 9,
+the status is the failure message that it prints out before exiting.
+
+ const assert : (cond : bool, fmt : byte[:], args : ... -> void)
+
+Assert checks that condition is true. If it is not true, then the message
+is printed as in `std.fmt`, and the program is aborted with `suicide()`.
+
+ const suicide : ( -> void)
+
+Suicide aborts a program. It does not print any message, it simply sends
+the program a SIGABRT or segfaults it. All threads are terminated, and the
+program goes away.
+
+ generic try : (v : result(@a, @b) -> @a)
+ generic get : (v : option(@a) -> @a)
+
+Try and get both return the value from the successful branch of their type:
+`try` returns the value contained in `\`std.Ok`, and `get` returns the value
+in `\`std.Some`.
+
+If this does not match the union, a diagnostic message is printed and the
+program is aborted.
+
+ generic tryv : (v : result(@a, @b), default : @a -> @a)
+ generic getv : (v : option(@a), default : @a -> @a)
+
+Try and get both return the value from the successful branch of their type:
+`try` returns the value contained in `\`std.Ok`, and `get` returns the value
+in `\`std.Some`.
+
+If this does not match the union, the default value is returned to the
+caller, as though the type had contained `\`Some default` or `\`Ok default`
+
diff --git a/doc/api/libstd/files.txt b/doc/api/libstd/files.txt
new file mode 100644
index 0000000..f75e765
--- /dev/null
+++ b/doc/api/libstd/files.txt
@@ -0,0 +1,369 @@
+{
+ title: File Handling
+ description: libstd: File Handling
+}
+
+File Handling
+-------------
+
+
+ pkg std =
+ type dir = struct
+ ;;
+
+ /* seek options */
+ const Seekset : whence
+ const Seekcur : whence
+ const Seekend : whence
+
+ /* open options */
+ const Ordonly : fdopt
+ const Owronly : fdopt
+ const Ordwr : fdopt
+ const Otrunc : fdopt
+ const Ocreat : fdopt
+ const Oappend : fdopt
+ const Odir : fdopt
+
+ /* directory handling */
+ const diropen : (p : byte[:] -> std.result(dir#, byte[:]))
+ const dirread : (d : dir# -> std.option(byte[:]))
+ const dirclose : (d : dir# -> void)
+ const dirname : (p : byte[:] -> byte[:])
+ const mkdir : (path : byte[:], mode : int64 -> int64)
+ const mkpath : (p : byte[:] -> bool)
+ const chdir : (path : byte[:] -> bool)
+
+ /* file handling */
+ const open : (path : byte[:], opts : fdopt -> std.result(fd, errno))
+ const openmode : (path : byte[:], opts : fdopt, mode : int64 -> std.result(fd, errno))
+ const mktemp : (base : byte[:], opts : fdopt, mode : int64 -> std.result((fd, byte[:]), errno)
+ const close : (fd : fd -> int64)
+ const creat : (path : byte[:], mode : int64 -> fd)
+ const read : (fd : fd, buf : byte[:] -> size)
+ const write : (fd : fd, buf : byte[:] -> size)
+ const seek : (fd : fd, delta : off, whence : whence -> off)
+ const pipe : (fds : fd[2]# -> int64)
+ const dup2 : (ofd : fd, nfd : fd -> fd)
+ const remove : (path : byte[:] -> bool)
+ const unlink : (path : byte[:] -> int)
+
+ /* path manipulation */
+ const basename : (p : byte[:] -> byte[:])
+ const pathcat : (a : byte[:], b : byte[:] -> byte[:])
+ const pathjoin : (p : byte[:][:] -> byte[:])
+ const pathnorm : (p : byte[:] -> byte[:])
+ const getcwd : (-> byte[:])
+
+ /* file properties */
+ const fmtime : (f : byte[:] -> result(time, errno))
+ const fsize : (f : byte[:] -> result(off, errno))
+ const fexists : (f : byte[:] -> bool)
+ const fisdir : (f : byte[:] -> bool)
+
+ /* convenience functions */
+ const slurp : (path : byte[:] -> result(byte[:], byte[:]))
+ const fslurp : (path : fd -> result(byte[:], byte[:]))
+ const blat : (path : byte[:], buf : byte[:], perm : int64 -> bool)
+ const fblat : (f : fd, buf : byte[:] -> bool)
+ ;;
+
+
+Data Types and Constants
+------------------------
+
+Libstd's file APIs are generally relatively thin wrappers around the host OS
+functions. They are a portable subset of this functionality, designed for both
+ease of use and portability.
+
+ type dir = struct
+ ;;
+
+The directory struct represents the current state of a directory walk.
+
+ /* seek options */
+ const Seekset : whence
+ const Seekcur : whence
+ const Seekend : whence
+
+These are a set of values which describe from where to seek within the file.
+
+ /* open options */
+ const Oread : fdopt
+ const Owrite : fdopt
+ const Ordwr : fdopt
+ const Otrunc : fdopt
+ const Ocreat : fdopt
+
+These are a set of options that are passed to the open variants describing
+what mode to open the file in. These are bit masks which be OR'ed together to
+combine them.
+
+`Oread` and `Owrite` request permission to read and write the file, respectively.
+`Ordwr` is a convenience flag which ors read and write together. `Otrunc`
+indicates that the file should be truncated to zero bytes in length before it
+is opened. `Ocreat` indicates that the file should be created if it does not
+exist instead of returning an error. `Odir` indicates that this file should
+be opened as a directory.
+
+`Ocreat` does not create the path leading to it, and will return an error if
+that path does not exist.
+
+
+Directories
+-----------
+
+ const diropen : (p : byte[:] -> std.result(dir#, byte[:]))
+
+The `diropen` function opens a path as a directory, and returns a pointer
+to an object which tracks the state of the directory, allowing for reading
+file names one by one from this directory.
+
+Returns: Either a directory wrapped up in the `\`Ok` branch of the result,
+or a string describing the failure reason in the `\`Fail` branch.
+
+ const dirread : (d : dir# -> result(option(byte[:]), errno)
+
+The `dirread` reads a single entry from the directory, opened with `diropen`,
+returning it as a string.
+
+Returns `\`Some entry`, or `\`None` at the end of the directory.
+
+ const dirclose : (d : dir# -> void)
+
+`dirclose` closes a directory for reading. This frees all associated
+resources, returning them to the system. The directory passed in must have
+been opened with `diropen`.
+
+Returns: Nothing.
+
+ const mkdir : (path : byte[:], mode : int64 -> errno)
+
+`mkdir` creates the directory specified in `path`. with the mode `mode`. It
+requires the parent directory to exist and be writable. Absolute paths will
+be created relative to the root of the file system, and relative paths will
+be created relative to the current working directory, as usual.
+
+If the directory already exists, this is counted as a failure to create the
+directory.
+
+Returns: Enone on success, or the error that caused the failure if it fails.
+
+ const mkpath : (path : byte[:] -> errno)
+
+`mkpath` creates a full path specified by `path`. It creates all of the
+path components required to create the full path specified. It requires
+the parent of the first directory entry that is not currently in existence
+to be writable.
+
+Absolute paths will be created relative to the root of the file system, and
+relative paths will be created relative to the current working directory, as
+usual.
+
+Returns: Enone on success, or the error that caused the directory creation
+to fail on failure.
+
+ const chdir : (path : byte[:] -> bool)
+
+Chdir changes the current working directory to the path specified in `path`.
+
+Returns: True on success, false on failure.
+
+Files
+-----
+
+ const open : (path : byte[:], opts : fdopt -> result(fd, errno))
+ const openmode : (path : byte[:], opts : fdopt, mode : int64 -> result(fd, errno))
+
+Open opens the file `path` for I/O according to the flags reqeusted in `opts`,
+and returns a result containing the file descriptor or error. The mode is a
+combination of the modes specified above: `Oread`, `Owrite`, `Ordwr`,
+`Otrunc`, or `Ocreat`.
+
+Openmode is similar to open, however, if Ocreate is passed, it the file
+requested will be created with the permissions passed in.
+
+Returns: either a valid file descriptor on success, or an error describing why
+the open failed on failure.
+
+ const mktemp : (base : byte[:], opts : fdopt, mode : int64 -> std.result((fd, byte[:]), errno)
+
+Mktemp is similar to openmode, however instead of taking a full path as
+its first argument, it generates a unique name for a file in temporary
+storage by appending a random string to the base name `base`. The mode
+provided is in addition to the implicit Ocreat | Oexcl.
+
+Returns: Either a successful result containing the tuple of file descriptor
+opened and path generated, or an error describing the failure. The path
+is allocated with `std.slalloc`, and must be freed by the caller using
+`std.slfree`. The file descriptor must be closed as ususal.
+
+
+ const close : (fd : fd -> void)
+
+Closes closes the file descriptor. If the file descriptor is valid, close
+is guaranteed to close it. If it is not valid, this is a no-op.
+
+ const read : (fd : fd, buf : byte[:] -> result(size, errno))
+
+Reads up to the length of `buf` from the file descriptor `fd`, at the current
+offset within the file, advancing the offset by the count of bytes read. The
+buffer may not be filled entirely when the read completes. For example, when
+reading from a console, often only one line will be returned at a time.
+
+Conventionally, `0` bytes are returned at the end of the file.
+
+Returns: Either the number of bytes read on success, or the error that caused
+a failure reading on failure.
+
+ const write : (fd : fd, buf : byte[:] -> result(size, errno))
+
+Write writes up to the length of `buf` to the file descriptor, writing the
+bytes at the current offset. The offset is advanced by the number of bytes
+that were written, extending the file size if necessary. Write is not
+guaranteed to write the full buffer.
+
+Returns: The number of bytes written on success, or the error that caused
+the failure on error.
+
+ const seek : (fd : fd, delta : off, whence : whence -> result(off, errno))
+
+Seek changes the current offset within the file descriptor `fd` by `delta`.
+This delta can be treated in three different ways, depending on the value of
+`whence.
+
+If `whence` is Seekset, then `delta` is treated as an absolute value to seek
+to, and the offset is set to `delta`. If `whence` is `Seekcur`, then `delta`
+is added to the current offset. If `whence` is `Seekend`, then `delta` is
+added to the size of the file.
+
+Returns: Either the new offset within the file on success, or the error that
+occurred on failure.
+
+ const pipe : (fds : fd[2]# -> errno)
+
+Pipe creates a unidirectional channel for communication between processes.
+Two file descriptors are returned in the array fd. Data written to fd[1] is
+available in fd[0]. The pipe may or may not be buffered.
+
+Returns: Enone on success, otherwise, returns the error that caused this
+call to fail.
+
+ const dup2 : (ofd : fd, nfd : fd -> result(fd, errno))
+
+Dup2 copies the old fd `ofd` to the new fd `nfd`. If the file descriptor
+`nfd` is already open, then it is implicitly closed by this call before
+the fd is copied. This is done atomically.
+
+Returns: Either the new fd used, on success, or an error describing the
+failure.
+
+ const remove : (path : byte[:] -> errno)
+
+Remove removes the file specified from the directory in which it is contained.
+The user must have write permissions for the directory in order to remove
+files from it. If `path` is a directory, it must be empty.
+
+Returns: Enone on success, otherwise the error that caused the failure.
+
+Path Manipulation
+-----------------
+ const basename : (p : byte[:] -> byte[:])
+ const dirname : (p : byte[:] -> byte[:])
+
+Given a string of the form "foo/bar/baz", `dirname()` returns the directory
+component of it -- in other words, everything up to the final `/`. It ignores
+trailing slashes. It
+
+The caller is responsible for freeing the path with `slfree`.
+
+For example, `dirname("foo/bar//")` will return `"foo/bar"`.
+
+ const pathcat : (a : byte[:], b : byte[:] -> byte[:])
+
+Pathcat joins two paths together, using '/' as a directory
+separator. The paths are normalized before being returned. This
+call is shorthand for `std.pathjoin([a, b][:])`.
+
+The caller is responsible for freeing the path with `slfree`.
+
+Returns: A concatenated path.
+
+ const pathjoin : (p : byte[:][:] -> byte[:])
+
+Pathcat joins a list of paths together, using '/' as a directory
+separator. The paths are normalized before being returned. This
+call is shorthand for `std.pathjoin([a, b][:])`.
+
+The caller is responsible for freeing the path with `slfree`.
+
+Returns: A concatenated path.
+
+ const pathnorm : (p : byte[:] -> byte[:])
+
+Pathnorm does a purely lexical normalization of the path. It removes
+redundant components, doubled `/` characters, and similar. The returned
+path is equivalent to the original input path.
+
+The caller is responsible for freeing the path with `slfree`.
+
+Returns: A new normalized path.
+
+ const getcwd : (-> byte[:])
+
+Returns the current working directory of the program. The caller is
+responsible for freeing the path with `slfree`.
+
+Returns: A string representing the working directory.
+
+File Properties
+---------------
+
+ const fmtime : (f : byte[:] -> result(time, errno))
+
+Returns either the last modification time of the file `f`, or
+the error that was encountered extracting this information.
+
+ const fsize : (f : byte[:] -> result(off, errno))
+
+Returns either the size in bytes of the file `f`, or
+the error that was encountered extracting this information.
+
+ const fexists : (f : byte[:] -> bool)
+
+Returns `true` if the file is able to be `stat`ed, or `false`
+if this fails.
+
+ const fisdir : (f : byte[:] -> bool)
+
+Returns `true` if the file is a directory that is able to be `stat`ed, or
+`false` if this fails.
+
+Convenience Functions
+---------------------
+
+ const slurp : (path : byte[:] -> result(byte[:], errno))
+
+Reads all bytes from `path` until the end of file.
+
+Returns either the file data, or the failure encountered.
+
+ const fslurp : (fd : fd -> result(byte[:], errno))
+
+Reads all bytes from the file descriptor `fd` until the end of file.
+
+Returns either the file data, or the failure encountered.
+
+ const blat : (path : byte[:], buf : byte[:], perm : int64 -> bool)
+
+Creates the file `path` with permissions `perm`, and writes as much of
+`buf` as it can into it.
+
+Returns Enone if no errors were encountered. Otherwise, the error is returned.
+
+ const fblat : (f : fd, buf : byte[:] -> bool)
+
+Writes as much of `buf` as it can into the file descriptor `fd`.
+
+Returns Enone if no errors were encountered. Otherwise, the error is returned.
+
diff --git a/doc/api/libstd/fmt.txt b/doc/api/libstd/fmt.txt
new file mode 100644
index 0000000..40f1b3f
--- /dev/null
+++ b/doc/api/libstd/fmt.txt
@@ -0,0 +1,201 @@
+{
+ title: Formatted I/O
+ description: libstd: Formatted I/O
+}
+
+
+
+Formatted I/O
+-------
+
+ pkg std =
+ /* output to file descriptors */
+ const put : (fmt : byte[:], args : ... -> size)
+ const fput : (fd : fd, fmt : byte[:], args : ... -> size)
+ const putv : (fmt : byte[:], ap : valist# -> size)
+ const fputv : (fd : fd, fmt : byte[:], ap : valist# -> size)
+
+ /* formatting values */
+ const fmt : (fmt : byte[:], args : ... -> byte[:])
+ const fmtv : (fmt : byte[:], ap : valist# -> byte[:])
+ const bfmt : (buf : byte[:], fmt : byte[:], args : ... -> byte[:])
+ const bfmtv : (buf : byte[:], fmt : byte[:], ap : valist# -> byte[:])
+ const sbfmt : (buf : strbuf#, fmt : byte[:], args : ... -> size)
+ const sbfmtv : (buf : strbuf#, fmt : byte[:], ap : valist# -> size)
+
+ /* custom formatting */
+ const fmtinstall : (ty : byte[:], \
+ fn : (sb : strbuf#, \
+ ap : valist#, \
+ opts : (byte[:],byte[:])[:] \
+ -> void), \
+ optdesc : (byte[:], bool)[:] \
+ -> void)
+ ;;
+
+
+Overview
+--------
+
+Formatting in Myrddin is done with format strings. These are effectively
+dynamically typed at runtime, using introspection to decide the best way to
+format a type. Custom formatters are allowed and encouraged.
+
+Formatting is specified with a `{}` pair, with any specifiers describing the
+formatting passed in as a comma separated set of key value pairs. For example,
+an integer can be padded with zeros and formatted in hex with the following
+format specifier: `{p=0,x}`. If you want a literal '{' character, it can
+be escaped by doubling it.
+
+None of the format specifiers print a newline character by default.
+
+Format Specifiers
+--------------------------
+
+The set of specifiers for the default types is sparse, and is fully
+specified below.
+
+<dl>
+ <dt>w=WIDTH</dt>
+ <dd><p>Fill out to at least width WIDTH, filling with pad
+ characters.</p></dd>
+
+ <dt>p=PAD</dt>
+ <dd>
+ <p>Fill spare width with this character. Defaults to a space
+ character.</p> </dd>
+
+ <dt>x</dt>
+ <dd>
+ <p>Format in hex. This is only valid for integer types.</p>
+ </dd>
+
+ <dt>j=joiner</dt>
+ <dd>
+ <p>Join slices with the joiner string. This leaves off the square
+ brackets from the ends, and replaces the default joiner string ", ".
+ </p>
+ </dd>
+</dl>
+
+Specifiers can be installed by custom specifiers, and can be any
+arbitrary set of strings.
+
+Functions
+---------
+
+All the format functions come in two variants: A variadic one, and one
+that takes a variadic argument list. The latter is present for ease of
+chaining from within a variadic function.
+
+ const put : (fmt : byte[:], args : ... -> size)
+ const fput : (fd : fd, fmt : byte[:], args : ... -> size)
+ const putv : (fmt : byte[:], ap : valist# -> size)
+ const fputv : (fd : fd, fmt : byte[:], ap : valist# -> size)
+
+The `put` set of functions will format and output to a file descriptor. For
+`put` and `putv`, the file descriptor is stdout. For `fput` and `fputv`, the
+file descriptor is the one that is provided.
+
+These functions write immediately, and do not buffer, although they do attempt
+to do their writing in a single system call, and will only split the call if
+the kernel indicates short writes.
+
+The `v` variants will take a variadic argument list for printing.
+
+Returns: the number of bytes written to the file descriptor.
+
+ const sbfmt : (buf : strbuf#, fmt : byte[:], args : ... -> size)
+ const sbfmtv : (buf : strbuf#, fmt : byte[:], ap : valist# -> size)
+
+The sbfmt functions will append to a string buffer, instead of writing to an
+output stream, but are otherwise similar to the `fmt` functions. These
+functions will return the number of bytes formatted. If the string buffer is
+statically sized, and gets filled, the truncated size will be returned.
+
+ const fmt : (fmt : byte[:], args : ... -> byte[:])
+ const fmtv : (fmt : byte[:], ap : valist# -> byte[:])
+
+These functions will format according to the format string, and return a
+freshly allocated string containing the formatted string. This string should
+be freed with `slfree`.
+
+ const bfmt : (buf : byte[:], fmt : byte[:], args : ... -> byte[:])
+ const bfmtv : (buf : byte[:], fmt : byte[:], ap : valist# -> byte[:])
+
+These functions will format according to the format string, putting the result
+into `buf`. They return a slice into the buffer array.
+
+ const fmtinstall : (ty : byte[:], \
+ fn : (sb : strbuf#,
+ ap : valist#, \
+ opts : (byte[:],byte[:])[:] \
+ -> void), \
+ optdesc : (byte[:], bool)[:] \
+ -> void)
+
+Fmtinstall installs a custom formatter for a type. The type `ty` is a type
+description that you would want to format. It can be obtained
+using `std.typeof(var)`, `fn` is a function that handles custom formatting,
+and `optdesc` is a list of options that this custom formater takes. It is
+in the form a list of strings -- the argument names -- and booleans that
+define whether these arguments take values.
+
+
+The custom formatter takes a string buffer `sb` which you are expected to
+format the custom input into, as well as a valist that you are expected to
+pull the value from. Finally, it takes an option list.
+
+If a formatter is already installed for a type, it is replaced.
+
+Examples
+--------
+
+This example demonstrates a bunch of formatting using the std.format API. It
+covers all of the various format specifiers, escaping, as well as showing the
+formatting of complex types.
+
+```{runmyr fmtsimple}
+use std
+
+const main = {
+ /* default formats */
+ std.put("{} {}\n", "abcd", 123)
+ std.put("{}\n", [1,2,3][:])
+ std.put("{}\n", (1,"foo"))
+
+ /* mix in some format options */
+ std.put("{w=10}\n", "abcd")
+ std.put("{p=0,w=10}\n", "abcdefghijkl")
+ std.put("{w=10,x}\n", 10)
+ std.put("{p=0,w=10}\n", 10)
+
+ /* and now some escaping */
+ std.put("{}bar{}\n", "foo\n", "baz")
+ std.put("{{}}bar{}\n", "baz")
+ std.put("{{bar{}}}\n", "baz")
+}
+```
+
+This example shows how you would set up a
+
+```{runmyr customfmt}
+use std
+
+const main = {
+ var x : int = 0 /* dummy: used for typeof */
+
+ std.fmtinstall(std.typeof(x), goaway, [][:])
+ std.put("custom format: {}\n", 0x41)
+}
+
+const goaway = {sb, ap, opts
+ var i : int64
+
+ i = std.vanext(ap)
+ std.sbfmt(sb, "go away! char val={}\n", i castto(char))
+}
+```
+
+
+
diff --git a/doc/api/libstd/index.txt b/doc/api/libstd/index.txt
new file mode 100644
index 0000000..f154f09
--- /dev/null
+++ b/doc/api/libstd/index.txt
@@ -0,0 +1,711 @@
+{
+ title: libstd
+ description: libstd: Summary
+}
+
+Libstd Summary
+---------------
+
+This is a summary page listing all of the functions and types available
+in libstd, sorted by category. The library is a bit of a grab bag of
+functionality, but a good chunk of what is needed will be built in to
+the library.
+
+
+#### [Memory Allocation](alloc)
+
+Memory allocation is a function that nearly every program needs
+to be able to do. Myrddin's generics allow for relatively easy
+to use and typesafe functions for this to be written.
+
+ pkg std =
+ generic mk : (val : @a -> @a#)
+ generic alloc : ( -> @a#)
+ generic zalloc : ( -> @a#)
+ generic free : (v:@a# -> void)
+ generic slalloc : (len : size -> @a[:])
+ generic slzalloc : (len : size -> @a[:])
+ generic slgrow : (sl : @a[:]#, len : size -> @a[:])
+ generic slzgrow : (sl : @a[:]#, len : size -> @a[:])
+ generic slfree : (sl : @a[:] -> void)
+ const bytealloc : (sz:size -> byte#)
+ const zbytealloc : (sz:size -> byte#)
+ const bytefree : (m:byte#, sz:size -> void)
+ ;;
+
+#### [Error Handling](err)
+
+Myrddin provides a number of types and operations for propagating errors
+for later handling.
+
+It also provides operations for throwing up your hands, setting yourself on
+fire, and screaming, if that's more appropriate.
+
+ pkg std =
+ type option(@a) = union
+ `Some @a
+ `None
+ ;;
+ pkg std =
+ type result(@a, @b) = union
+ `Ok @a
+ `Fail @b
+ ;;
+ ;;
+
+ $noret const fatalv : (fmt : byte[:], ap : valist# -> void)
+ $noret const fatal : (fmt : byte[:], args : ... -> void)
+ const assert : (cond : bool, fmt : byte[:], args : ... -> void)
+ const suicide : ( -> void)
+
+ generic try : (v : result(@a, @b) -> @a)
+ generic tryv : (v : result(@a, @b), d : @a -> @a)
+ generic get : (v : option(@a) -> @a)
+ generic getv : (v : option(@a), d : @a -> @a)
+ ;;
+
+#### [OS Interfaces](os)
+
+The OS interfaces cover some portable primitives for handling processes
+and OS errors. It restricts itself to a set of portable wrappers for OS
+functionality.
+
+For complete interfaces, the `sys` library is your friend, providing
+all OS functionality that can be provided.
+
+ pkg std =
+ type sysinfo = struct
+ system : byte[:]
+ version : byte[:]
+ release : byte[:]
+ arch : byte[:]
+ uname : sys.utsname /* storage */
+ ;;
+
+ type waitstatus = union
+ `Wsuccess
+ `Wfailure
+ `Wsignalled
+ `Waiterror
+ ;;
+
+ const Enone : errno
+ const Erange : errno
+ const Ebadf : errno
+ const Eexist : errno
+ const Einval : errno
+ const Efault : errno
+ const Eio : errno
+ const Emisc : errno
+
+ const getsysinfo : (si : sysinfo# -> void)
+ const execvp : (cmd : byte[:], args : byte[:][:] -> int64)
+ const execvpe : (cmd : byte[:], args : byte[:][:], env : byte[:][:] -> int64)
+ const getenv : (name : byte[:] -> option(byte[:]))
+ const getenvv : (name : byte[:], default : byte[:] -> byte[:])
+ const getpid : ( -> pid)
+ const fork : (-> pid)
+ const exec : (cmd : byte[:], args : byte[:][:] -> int64)
+ const execve : (cmd : byte[:], args : byte[:][:], env : byte[:][:] -> int64)
+ const waitpid : (pid:pid, loc:int32#, opt : int64 -> int64)
+ const spork : (cmd : byte[:][:] -> result((pid, fd, fd), int))
+ const sporkfd : (cmd : byte[:][:], infd : fd, outfd : fd -> result(pid, int))
+ const exit : (status:int -> void)
+ const wait : (pid : pid -> waitstatus)
+ ;;
+
+#### [File Handling](files)
+
+Many programs do file i/o by default. This package provides a portable
+interface to the common subset that most programs need and most OSes provide.
+
+ pkg std =
+ type dir = struct
+ ;;
+
+ /* seek options */
+ const Seekset : whence
+ const Seekcur : whence
+ const Seekend : whence
+
+ /* open options */
+ const Ordonly : fdopt
+ const Owronly : fdopt
+ const Ordwr : fdopt
+ const Otrunc : fdopt
+ const Ocreat : fdopt
+ const Oappend : fdopt
+ const Odir : fdopt
+
+ /* directory handling */
+ const diropen : (p : byte[:] -> std.result(dir#, byte[:]))
+ const dirread : (d : dir# -> std.option(byte[:]))
+ const dirclose : (d : dir# -> void)
+ const dirname : (p : byte[:] -> byte[:])
+ const mkdir : (path : byte[:], mode : int64 -> int64)
+ const mkpath : (p : byte[:] -> bool)
+ const chdir : (path : byte[:] -> bool)
+
+ /* file handling */
+ const open : (path : byte[:], opts : fdopt -> fd)
+ const openmode : (path : byte[:], opts : fdopt, mode : int64 -> fd)
+ const close : (fd : fd -> int64)
+ const creat : (path : byte[:], mode : int64 -> fd)
+ const read : (fd : fd, buf : byte[:] -> size)
+ const write : (fd : fd, buf : byte[:] -> size)
+ const seek : (fd : fd, delta : off, whence : whence -> off)
+ const pipe : (fds : fd[2]# -> int64)
+ const dup2 : (ofd : fd, nfd : fd -> fd)
+ const remove : (path : byte[:] -> bool)
+ const unlink : (path : byte[:] -> int)
+
+ /* path manipulation */
+ const basename : (p : byte[:] -> byte[:])
+ const pathcat : (a : byte[:], b : byte[:] -> byte[:])
+ const pathjoin : (p : byte[:][:] -> byte[:])
+ const pathnorm : (p : byte[:] -> byte[:])
+ const getcwd : (-> byte[:])
+
+ /* file properties */
+ const fmtime : (f : byte[:] -> option(time))
+ const fsize : (f : byte[:] -> option(off))
+ const fexists : (f : byte[:] -> bool)
+ const fisdir : (f : byte[:] -> bool)
+
+ /* convenience functions */
+ const slurp : (path : byte[:] -> result(byte[:], byte[:]))
+ const fslurp : (path : fd -> result(byte[:], byte[:]))
+ const blat : (path : byte[:], buf : byte[:], perm : int64 -> bool)
+ const fblat : (f : fd, buf : byte[:] -> bool)
+ ;;
+
+#### [Networking](networking)
+
+The networking related functionality in libstd provides the ability to
+quickly and easily open file descriptors to a server, as well as to
+resolve servers and handle IP parsing.
+
+Currently, there is a large hole in functionality for announcing and
+serving, where raw system specific network APIs neeed to be used
+from the `sys` library.
+
+ pkg std =
+ type rectype = union
+ `DnsA /* host address */
+ `DnsNS /* authoritative name server */
+ `DnsCNAME /* canonical name for an alias */
+ `DnsSOA /* marks the start of a zone of authority */
+ `DnsWKS /* well known service description */
+ `DnsPTR /* domain name pointer */
+ `DnsHINFO /* host information */
+ `DnsMINFO /* mailbox or mail list information */
+ `DnsMX /* mail exchange */
+ `DnsTXT /* text strings */
+ `DnsAAAA /* ipv6 host address */
+ ;;
+
+ type resolveerr = union
+ `Badhost
+ `Badsrv
+ `Badquery
+ `Badresp
+ ;;
+
+ type hostinfo = struct
+ fam : sys.sockfam
+ stype : sys.socktype
+ ttl : uint32
+ addr : ipaddr
+ ;;
+
+ type ipaddr = union
+ `Ipv4 byte[4]
+ `Ipv6 byte[16]
+ ;;
+
+ /* network connections */
+ const dial : (dialstr : byte[:] -> result(fd, byte[:]))
+ const resolve : (host : byte[:] -> result(hostinfo[:], resolveerr))
+ const resolvemx : (host : byte[:] -> result(hostinfo[:], resolveerr))
+ const resolverec : (host : byte[:], t : rectype -> result(hostinfo[:], resolveerr))
+
+ /* ip parsing */
+ const ipparse : (ip : byte[:] -> option(ipaddr))
+ const ip4parse : (ip : byte[:] -> option(ipaddr))
+ const ip6parse : (ip : byte[:] -> option(ipaddr))
+
+ generic hosttonet : (v : @a -> @a)
+ generic nettohost : (v : @a -> @a)
+ ;;
+
+#### [Command Line Parsing](cli)
+
+Simple command line parsing is offered, designed to meet the needs of most
+programs quickly and easily. There isn't much to say here.
+
+ pkg std =
+ type optdef = struct
+ argdesc : byte[:] /* the description for the usage */
+ minargs : std.size /* the minimum number of positional args */
+ maxargs : std.size /* the maximum number of positional args (0 = unlimited) */
+ noargs : std.bool /* whether we accept args at all */
+ opts : optdesc[:] /* the description of the options */
+ ;;
+ type optdesc = struct
+ opt : char
+ arg : byte[:]
+ desc : byte[:]
+ optional : bool
+ ;;
+ type optparsed = struct
+ opts : (char, byte[:])[:]
+ args : byte[:][:]
+ ;;
+
+ const optparse : (optargs : byte[:][:], def : optdef# -> optparsed)
+ const optusage : (prog : byte[:], def : optdef# -> void)
+ ;;
+
+#### [Formatted Output](fmt)
+
+libstd supports a number of simple, easy to use formatting functions,
+which can provide a sane format for any type out of the box, but also
+support custom formatters for specific types, so that they can be pretty
+printed. Many of the builtin types, such as bigints, install custom
+formatters by default.
+
+ pkg std =
+ /* output to file descriptors */
+ const put : (fmt : byte[:], args : ... -> size)
+ const fput : (fd : fd, fmt : byte[:], args : ... -> size)
+ const putv : (fmt : byte[:], ap : valist# -> size)
+ const fputv : (fd : fd, fmt : byte[:], ap : valist# -> size)
+
+ /* formatting values */
+ const fmt : (fmt : byte[:], args : ... -> byte[:])
+ const fmtv : (fmt : byte[:], ap : valist# -> byte[:])
+ const bfmt : (buf : byte[:], fmt : byte[:], args : ... -> byte[:])
+ const bfmtv : (buf : byte[:], fmt : byte[:], ap : valist# -> byte[:])
+ const sbfmt : (buf : strbuf#, fmt : byte[:], args : ... -> size)
+ const sbfmtv : (buf : strbuf#, fmt : byte[:], ap : valist# -> size)
+
+ /* custom formatting */
+ const fmtinstall : (ty : byte[:], \
+ fn : (sb : strbuf#, \
+ ap : valist#, \
+ opts : (byte[:],byte[:])[:] \
+ -> void), \
+ optdesc : (byte[:], bool)[:] \
+ -> void)
+ ;;
+
+#### [Iteration Utilities](iterutil)
+
+Support for some common iteration patterns is provided. There are many kinds
+of iteration that are done, and some of the most common ones are directly
+supported by libstd.
+
+There are a few syntactic(!) issues in the language preventing these from
+taking arbitrary iterators as parameters, but when this is resolved, that
+restriction will be lifted.
+
+ pkg std =
+ type zipiter(@a, @b)
+ type reverseiter(@a)
+ type enumiter(@a)
+
+ impl iterable zipiter(@a, @b) -> (@a, @b)
+ impl iterable enumiter(@a) -> (size, @a)
+ impl iterable reverseiter(@a) -> @a
+
+ generic byzip : (a : @a[:], b : @b[:] -> zipiter(@a, @b))
+ generic byenum : (a : @a[:] -> enumiter(@a))
+ generic byreverse : (sl : @a[:] -> reverseiter(@a))
+ ;;
+
+
+#### [Variadic Arguments](varargs)
+
+Myrddin supports variadic arguments for functions, and allows you
+to walk over them, similar to the varargs functions in C, but safer.
+
+In addition, the Myrddin compiler generates type information for the types
+that are compiled into a program. This code provides a rather awkward API
+for iterating over them, and inspecting their values.
+
+ pkg std =
+ type typedesc = union
+ `Tynone
+
+ /* atomic types */
+ `Tyvoid
+ `Tybool
+ `Tychar
+
+ `Tyint8
+ `Tyint16
+ `Tyint
+ `Tyint32
+ `Tyint64
+
+ `Tybyte
+ `Tyuint8
+ `Tyuint16
+ `Tyuint
+ `Tyuint32
+ `Tyuint64
+ `Tyflt32
+ `Tyflt64
+ `Tyvalist
+
+ /* compound types */
+ `Typtr byte[:]
+ `Tyfunc typecursor
+ `Tyslice byte[:]
+ `Tyarray (size, byte[:])
+
+ /* aggregate types */
+ `Tytuple typecursor
+ `Tystruct typecursor
+ `Tyunion typecursor
+ /* name info */
+ `Tyname (byte[:], byte[:])
+ ;;
+ type typecursor = struct
+ nelt : size
+ rem : byte[:]
+ isnamed : bool
+ isiter : bool
+ ;;
+ type typeinfo = struct
+ size : size
+ align : size
+ ;;
+ generic typeof : (v : @a -> byte[:])
+ const typeenc : (p : ...# -> typecursor)
+ const typeenccursor : (e : byte[:] -> typecursor)
+ const typedesc : (e : byte[:] -> typedesc)
+ const typeinfo : (e : byte[:] -> typeinfo)
+ const tcnext : (t : typecursor# -> byte[:])
+ const tcpeek : (t : typecursor# -> byte[:])
+ const ncpeek : (t : typecursor# -> (byte[:], byte[:]))
+ const ncnext : (t : typecursor# -> (byte[:], byte[:]))
+
+ const vastart : (args : ...# -> valist)
+ const vatype : (ap : valist# -> byte[:])
+ const vabytes : (ap : valist# -> byte[:])
+ const vaenter : (ap : valist# -> valist)
+ generic vanext : (ap : valist# -> @a)
+ ;;
+
+#### [Slice manipulation](slices)
+
+Slices are used everywhere within Myrddin code, so clearly we have
+some functions to manipulate them. They're listed here. Boopity boopity
+boo.
+
+ pkg std =
+ generic sleq : (a : @a[:], b : @a[:] -> bool)
+ generic slcp : (a : @a[:], b : @a[:] -> void)
+ generic slput : (sl : @a[:], idx : size, elt : @a -> @a[:])
+ generic slpush : (sl : @a[:]#, elt : @a -> @a[:])
+ generic sldup : (sl : @a[:] -> @a[:])
+ generic slfill : (sl : @a[:], v : @a -> @a[:])
+ generic sljoin : (dst : @a[:]#, src : @a[:] -> @a[:])
+ ;;
+
+#### [String Manipulation](strings)
+
+String manipulation also tends to show up in code sometimes. Here are some
+functions that do that. These are all unicode aware, and will not corrupt
+utf8 data.
+
+ pkg std =
+ /* string buffers */
+ type strbuf = struct
+ ;;
+
+ const mksb : (-> strbuf#)
+ const mkbufsb : (buf : byte[:] -> strbuf#)
+ const sbfin : (sb : strbuf# -> byte[:])
+ const sbfree : (sb : strbuf# -> void)
+ const sbpeek : (sb : strbuf# -> byte[:])
+ const sbputc : (sb : strbuf#, v : char -> bool)
+ const sbputs : (sb : strbuf#, v : byte[:] -> bool)
+ const sbputb : (sb : strbuf#, v : byte -> bool)
+ const sbtrim : (sb : strbuf#, len : size -> void)
+
+ /* string searching */
+ const strfind : (haystack : byte[:], needle : byte[:] -> option(size))
+ const strrfind : (haystack : byte[:], needle : byte[:] -> option(size))
+ const strhas : (haystack : byte[:], needle : byte[:] -> bool)
+ const hasprefix : (s : byte[:], pre : byte[:] -> bool)
+ const hassuffix : (s : byte[:], suff : byte[:] -> bool)
+
+ /* C strings */
+ const cstrlen : (buf : byte[:] -> size)
+ const cstrconv : (buf : byte[:] -> byte[:])
+ const cstrconvp : (p : byte# -> byte[:])
+
+ /* tokenizing and splitting */
+ const strsplit : (s : byte[:], delim : byte[:] -> byte[:][:])
+ const strtok : (s : byte[:] -> byte[:][:])
+
+ /* string joining and stripping */
+ const strcat : (a : byte[:], b : byte[:] -> byte[:])
+ const strjoin : (strings : byte[:][:], delim : byte[:] -> byte[:])
+ const strstrip : (str : byte[:] -> byte[:])
+ const strfstrip : (str : byte[:] -> byte[:])
+ const strrstrip : (str : byte[:] -> byte[:])
+
+ /* parsing numbers out of strings */
+ generic intparsebase : (s : byte[:], base : int -> option(@a::(integral,numeric)))
+ generic intparse : (s : byte[:] -> option(@a::(integral,numeric)))
+ generic charval : (c : char, base : int -> @a::(integral,numeric))
+ ;;
+
+#### [Unicode](unicode)
+
+A bunch of predicates and conversions to handle unicode. This only
+provides simple functionality. For canonicalization, collation, and
+all of the other UAX algorithms, go look in.. oh, who am I kidding.
+I haven't had a chance to write them yet.
+
+ pkg std =
+ const Badchar : char
+ const Maxcharlen : size
+ const Maxcharval : char
+
+ /* utf8 information */
+ const charlen : (chr : char -> size)
+ const encode : (buf : byte[:], chr : char -> size)
+ const decode : (buf : byte[:] -> char)
+ const striter : (str : byte[:] -> (char, byte[:]))
+
+ /* character class predicates */
+ const isalpha : (c : char -> bool)
+ const isdigit : (c : char -> bool)
+ const isxdigit : (c : char -> bool)
+ const isnum : (c : char -> bool)
+ const isalnum : (c : char -> bool)
+ const isspace : (c : char -> bool)
+ const isblank : (c : char -> bool)
+ const islower : (c : char -> bool)
+ const isupper : (c : char -> bool)
+ const istitle : (c : char -> bool)
+
+ /* character class conversions */
+ const tolower : (c : char -> char)
+ const toupper : (c : char -> char)
+ const totitle : (c : char -> char)
+ ;;
+
+#### [Pervasive Data Structures](datastruct)
+
+There are some data structures that basically every program seems to use:
+Sets, and hash tables. Libstd includes them for that reason.
+
+ pkg std =
+ type bitset = struct
+ ;;
+
+ type htab(@k, @v) = struct
+ ;;
+
+ type htkviter(@k, @v)
+ impl iterable htkviter
+
+ type bsiter = struct
+ impl iterable bsiter
+
+ /* bit sets */
+ const mkbs : (-> bitset#)
+ const bsdup : (bs : bitset# -> bitset#)
+ const bsfree : (bs : bitset# -> void)
+ const bsmax : (a : bitset# -> size)
+ const bscount : (a : bitset# -> size)
+ generic bsput : (bs : bitset#, v : @a::(integral,numeric) -> bool)
+ generic bsdel : (bs : bitset#, v : @a::(integral,numeric) -> bool)
+ generic bshas : (bs : bitset#, v : @a::(integral,numeric) -> bool)
+ const bsdiff : (a : bitset#, b : bitset# -> void)
+ const bsintersect : (a : bitset#, b : bitset# -> void)
+ const bsunion : (a : bitset#, b : bitset# -> void)
+ const bseq : (a : bitset#, b : bitset# -> bool)
+ const bsissubset : (a : bitset#, b : bitset# -> bool)
+ const bsclear : (bs : bitset# -> bitset#)
+ const bybsvalue : (bs : bitset# -> bsiter)
+
+ /* hash tables */
+ generic mkht : (h : (k : @k -> uint32), eq : (a : @k, b : @k -> bool) -> htab(@k, @v)#)
+ generic htfree : (ht : htab(@k, @v)# -> void)
+ generic htput : (ht : htab(@k, @v)#, k : @k, v : @v -> void)
+ generic htdel : (ht : htab(@k, @v)#, k : @k -> void)
+ generic htget : (ht : htab(@k, @v)#, k : @k -> option(@v))
+ generic htgetv : (ht : htab(@k, @v)#, k : @k, fallback : @v-> @v)
+ generic hthas : (ht : htab(@k, @v)#, k : @k -> bool)
+ generic htkeys : (ht : htab(@k, @v)# -> @k[:])
+ generic byhtkeyvals : (ht : htab(@k, @v)# -> htkviter(@k, @v))
+
+ /* prepackaged hashing and equality tests */
+ const strhash : (s : byte[:] -> uint32)
+ const streq : (a : byte[:], b : byte[:] -> bool)
+ generic ptrhash : (p : @a# -> uint32)
+ generic ptreq : (a : @a#, b : @a# -> bool)
+ generic inthash : (v : @a::(integral,numeric) -> uint32)
+ generic inteq : (a : @a::(integral,numeric), b : @a::(integral,numeric) -> bool)
+ generic slhash : (sl : @a[:] -> uint32)
+ ;;
+
+
+#### [Pervasive Algorithms](algorithms)
+
+Many programs also use sorting and searching, so this is also provided by
+libstd. In addition, we package some useful comparison and hashing functions
+
+ pkg std =
+ /* the result of a comparison */
+ type order = union
+ `Before
+ `Equal
+ `After
+ ;;
+
+ /* sorting and searching */
+ generic sort : (sl:@a[:], cmp:(a:@a, b:@a -> order) -> @a[:])
+ generic lsearch : (sl : @t[:], val : @t, cmp : (a : @t, b : @t -> order) -> option(@idx::(integral,numeric)))
+ generic bsearch : (sl : @t[:], val : @t, cmp : (a : @t, b : @t -> order) -> option(@idx::(integral,numeric)))
+ generic swap : (a : @a#, b : @a# -> void)
+
+ /* prepackaged comparisons */
+ generic numcmp : (a : @a, b : @a -> order)
+ const strcmp : (a : byte[:], b : byte[:] -> order)
+ const strncmp : (a : byte[:], b : byte[:], n : size -> order)
+
+ /* extrema and absolute values */
+ generic min : (a : @a::numeric, b : @a::numeric -> @a::numeric)
+ generic max : (a : @a::numeric, b : @a::numeric -> @a::numeric)
+ generic clamp : (a : @a::numeric, min : @a::numeric, max : @a::numeric -> @a::numeric)
+ generic abs : (a : @a::numeric -> @a::numeric)
+ ;;
+
+#### [Randomness](randomness)
+
+And of course, you can't go without being a little random at times.
+
+ pkg std =
+ const mksrng : (seed : uint32 -> rng#)
+ const freerng : (rng : rng# -> void)
+ generic rand : (lo : @a::(numeric,integral), hi : @a::(numeric,integral) -> @a::(numeric,integral))
+ generic rngrand : (rng : rng#, lo : @a::(numeric,integral), hi : @a::(numeric,integral) -> @a::(numeric,integral))
+ generic rngrandnum : (rng : rng# -> @a::(numeric,integral))
+ const rngrandbytes : (rng : rng#, buf : byte[:] -> size)
+ ;;
+
+#### [Bigint Operations](bigint)
+
+While bigint usage in most programs is relatively rare, libstd needs them
+internally for handling floats, and several other widely used pieces of
+functionality also need them.
+
+As they are a significant amount of code, I decided it made sense to
+expose them in the public API.
+
+ pkg std =
+ type bigint = struct
+ ;;
+
+ generic mkbigint : (v : @a::(numeric,integral) -> bigint#)
+ const bigfree : (a : bigint# -> void)
+ const bigdup : (a : bigint# -> bigint#)
+ const bigassign : (d : bigint#, s : bigint# -> bigint#)
+ const bigmove : (d : bigint#, s : bigint# -> bigint#)
+ const bigparse : (s : byte[:] -> option(bigint#))
+ const bigclear : (a : bigint# -> bigint#)
+ const bigbfmt : (b : byte[:], a : bigint#, base : int -> size)
+ const bigtoint : (a : bigint# -> @a::(numeric,integral))
+ const bigiszero : (a : bigint# -> bool)
+ const bigeq : (a : bigint#, b : bigint# -> bool)
+ const bigcmp : (a : bigint#, b : bigint# -> order)
+ const bigadd : (a : bigint#, b : bigint# -> bigint#)
+ const bigsub : (a : bigint#, b : bigint# -> bigint#)
+ const bigmul : (a : bigint#, b : bigint# -> bigint#)
+ const bigdiv : (a : bigint#, b : bigint# -> bigint#)
+ const bigmod : (a : bigint#, b : bigint# -> bigint#)
+ const bigdivmod : (a : bigint#, b : bigint# -> (bigint#, bigint#))
+ const bigshl : (a : bigint#, b : bigint# -> bigint#)
+ const bigshr : (a : bigint#, b : bigint# -> bigint#)
+ const bigmodpow : (b : bigint#, e : bigint#, m : bigint# -> bigint#)
+ const bigpow : (a : bigint#, b : bigint# -> bigint#)
+ generic bigeqi : (a : bigint#, b : @a::(numeric,integral) -> bool)
+ generic bigaddi : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ generic bigsubi : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ generic bigmuli : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ generic bigdivi : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ generic bigshli : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ generic bigshri : (a : bigint#, b : @a::(integral,numeric) -> bigint#)
+ const bigpowi : (a : bigint#, b : uint64 -> bigint#)
+ ;;
+
+
+#### [Closures](closures)
+
+There are functions for heapifying closures, too.
+
+ pkg std =
+ generic fndup : (fn : @fn::function -> @fn::function)
+ generic fnfree : (fn : @fn::function -> void)
+ ;;
+
+#### [Misc](misc)
+
+Well, I said it was a grab bag. These don't really fit into any overarching
+category.
+
+ pkg std =
+ generic KiB : @a::(integral,numeric)
+ generic MiB : @a::(integral,numeric)
+ generic GiB : @a::(integral,numeric)
+ generic TiB : @a::(integral,numeric)
+ generic PiB : @a::(integral,numeric)
+ generic EiB : @a::(integral,numeric)
+ generic ZiB : @a::(integral,numeric)
+ generic YiB : @a::(integral,numeric)
+
+ generic Sec : @a::(integral,numeric)
+ generic Msec : @a::(integral,numeric)
+ generic Usec : @a::(integral,numeric)
+
+ /* time */
+ const now : (-> time)
+
+ /* packing integers */
+ generic putle64 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putbe64 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putle32 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putbe32 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putle16 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putbe16 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putle8 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putbe8 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+
+ /* unpacking integers */
+ generic getle64 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getbe64 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getle32 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getbe32 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getle16 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getbe16 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getle8 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getbe8 : (buf : byte[:] -> @a::(numeric,integral))
+
+ /* exploding and stitching floats */
+ const flt64bits : (flt : flt64 -> int64)
+ const flt32bits : (flt : flt32 -> int32)
+ const flt64frombits : (bits : uint64 -> flt64)
+ const flt32frombits : (bits : uint32 -> flt32)
+ const flt64explode : (flt : flt64 -> (bool, int64, int64))
+ const flt32explode : (flt : flt32 -> (bool, int32, int32))
+ const flt64stitch : (flt : flt64 -> (bool, int64, int64))
+ const flt32stitch : (flt : flt32 -> (bool, int32, int32))
+
+ ;;
diff --git a/doc/api/libstd/iterutil.txt b/doc/api/libstd/iterutil.txt
new file mode 100644
index 0000000..a023542
--- /dev/null
+++ b/doc/api/libstd/iterutil.txt
@@ -0,0 +1,100 @@
+{
+ title: libstd
+ description: libstd: Summary
+}
+
+
+Iteration Utilities
+----------------
+
+ pkg std =
+ type zipiter(@a, @b)
+ type reverseiter(@a)
+ type enumiter(@a)
+
+ impl iterable zipiter(@a, @b) -> (@a, @b)
+ impl iterable enumiter(@a) -> (size, @a)
+ impl iterable reverseiter(@a) -> @a
+
+ generic byzip : (a : @a[:], b : @b[:] -> zipiter(@a, @b))
+ generic byenum : (a : @a[:] -> enumiter(@a))
+ generic byreverse : (sl : @a[:] -> reverseiter(@a))
+ ;;
+
+Summary
+--------
+
+Iteration over sequence is something that turns up regularly. The iteration
+utilities provided here simplify a number of common instances of iteration
+over collections. They allow for iterating over a zipped sequence,
+enumerating a collection of elements, or going over a collection of elements
+in reverse.
+
+All of these iterators hold a reference to the original data, and require that
+it not be freed until they are finished consuming it. They do not copy the
+slices that they iterate over, and all consume O(1) storage.
+
+Zip Iterators
+-------------
+
+ impl iterable zipiter(@a, @b) -> (@a, @b)
+ generic byzip : (a : @a[:], b : @b[:] -> zipiter(@a, @b))
+
+The zipiter takes two slices, and returns an iterator that will walk over them
+pair by pair. So, for example, if you were to call `std.byzip([1,2,3][:],
+[4,5,6][:])`, you would iterate through the elements `(1,4), (2,5), (3, 6)`.
+
+Zip Iterators
+-------------
+ impl iterable enumiter(@a) -> (size, @a)
+ generic byenum : (a : @a[:] -> enumiter(@a))
+
+The enumiter takes a single sequence, and returns the pair of (index, element)
+tuples. If you were to call `std.byenum(["hello", "there", "friend"][:])`, you
+iterate through the elements `(1, "hello"), (2, "there"), (3, "friend")`.
+
+Reverse Iterators
+-----------------
+
+ impl iterable reverseiter(@a) -> @a
+ generic byreverse : (sl : @a[:] -> reverseiter(@a))
+
+
+The reverse takes a single slice, and returns the same elements back, but in
+the reverse order. Calling `std.byenum(["hello", "there", "friend"][:])`
+would iterate in the sequence `"friend", "there", "hello".
+
+Examples
+--------
+
+This is a simple zip iterator:
+
+```{runmyr zipiter}
+use std
+const main = {
+ for x in std.byzip([1,2,3][:], [4,5,6][:])
+ std.put("{}\n", x)
+ ;;
+}
+```
+
+This is a simple enum iterator:
+
+```{runmyr enumiter}
+use std
+const main = {
+ for x in std.byenum(["hello", "world"][:])
+ std.put("{}\n", x)
+ ;;
+}
+```
+
+```{runmyr reverseiter}
+use std
+const main = {
+ for x in std.byreverse(["hello", "world"][:])
+ std.put("{}\n", x)
+ ;;
+}
+```
+
diff --git a/doc/api/libstd/misc.txt b/doc/api/libstd/misc.txt
new file mode 100644
index 0000000..13ab478
--- /dev/null
+++ b/doc/api/libstd/misc.txt
@@ -0,0 +1,107 @@
+{
+ title: Misc
+ description: libstd: Misc
+}
+
+Misc
+-----
+
+The mongrels and mutts of libstd.
+
+ pkg std =
+ generic KiB : @a::(integral,numeric)
+ generic MiB : @a::(integral,numeric)
+ generic GiB : @a::(integral,numeric)
+ generic TiB : @a::(integral,numeric)
+ generic PiB : @a::(integral,numeric)
+ generic EiB : @a::(integral,numeric)
+ generic ZiB : @a::(integral,numeric)
+ generic YiB : @a::(integral,numeric)
+
+ /* time */
+ const now : (-> time)
+
+ /* packing integers */
+ generic putle64 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putbe64 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putle32 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putbe32 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putle16 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putbe16 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putle8 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putbe8 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+
+ /* unpacking integers */
+ generic getle64 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getbe64 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getle32 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getbe32 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getle16 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getbe16 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getle8 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getbe8 : (buf : byte[:] -> @a::(numeric,integral))
+
+ /* exploding and stitching floats */
+ const flt64bits : (flt : flt64 -> int64)
+ const flt32bits : (flt : flt32 -> int32)
+ const flt64frombits : (bits : uint64 -> flt64)
+ const flt32frombits : (bits : uint32 -> flt32)
+ const flt64explode : (flt : flt64 -> (bool, int64, int64))
+ const flt32explode : (flt : flt32 -> (bool, int32, int32))
+ const flt64stitch : (flt : flt64 -> (bool, int64, int64))
+ const flt32stitch : (flt : flt32 -> (bool, int32, int32))
+
+ ;;
+
+Constants
+----------
+
+ generic KiB : @a::(integral,numeric)
+ generic MiB : @a::(integral,numeric)
+ generic GiB : @a::(integral,numeric)
+ generic TiB : @a::(integral,numeric)
+ generic PiB : @a::(integral,numeric)
+ generic EiB : @a::(integral,numeric)
+ generic ZiB : @a::(integral,numeric)
+ generic YiB : @a::(integral,numeric)
+
+ generic Sec : time
+ generic Msec : time
+ generic Usec : time
+
+These are just constants that you can multiply things by in order to scale
+units. If you want to get a
+
+ const now : (-> time)
+
+Returns the current time in signed microseconds since the Unix epoch. Can
+represent dates approximatelyl 70,000 years in either direction from the
+present.
+
+ generic putle64 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putbe64 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putle32 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putbe32 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putle16 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putbe16 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putle8 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+ generic putbe8 : (buf : byte[:], v : @a::(numeric,integral) -> size)
+
+These functions pack integers into buffers. The suffix describes the number of
+bits that will be packed -- the values will be implicitly converted to an
+integer that is `nbits` long before packing into the buffer. Signed integers
+will be sign extended, and unsigned ones will be zero extended.
+
+ generic getle64 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getbe64 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getle32 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getbe32 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getle16 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getbe16 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getle8 : (buf : byte[:] -> @a::(numeric,integral))
+ generic getbe8 : (buf : byte[:] -> @a::(numeric,integral))
+
+These functions unpack integers from buffers. The numeric suffix describes how
+many bits will be extracted from the buffer. The value will implicitly be
+truncated or widened to the result returned by the specialization of this
+function.
diff --git a/doc/api/libstd/networking.txt b/doc/api/libstd/networking.txt
new file mode 100644
index 0000000..0229573
--- /dev/null
+++ b/doc/api/libstd/networking.txt
@@ -0,0 +1,181 @@
+{
+ title: Networking
+ description: libstd: Networking
+}
+Networking
+----------
+
+ pkg std =
+ type netaddr = union
+ `Ipv4 byte[4]
+ `Ipv6 byte[16]
+ ;;
+
+
+ /* network connections */
+ const dial : (dialstr : byte[:] -> result(fd, byte[:]))
+ const announce : (ds : byte[:] -> result(fd, byte[:]))
+ const listen : (sock : fd -> result(fd, byte[:]))
+ const accept : (lfd : fd -> result(fd, byte[:]))
+
+ /* ip parsing */
+ const ipparse : (ip : byte[:] -> option(netaddr))
+ const ip4parse : (ip : byte[:] -> option(netaddr))
+ const ip6parse : (ip : byte[:] -> option(netaddr))
+
+ generic hosttonet : (v : @a -> @a)
+ generic nettohost : (v : @a -> @a)
+ ;;
+
+Summary
+-------
+
+This group of functions contains the basic, portable networking functionality
+provided by libstd. There are other packages shipped which provide access
+to the underlying functionality used to implement this code, and which may
+provide more control.
+
+The bulk of the functionality is fairly low level. Most of the client side
+networking should be done using nothing more than `dial` and `announce`
+
+Dial describes the endpoint to connect to in the form `proto!host!service`.
+`proto` can be any supported protocol. The host specified can be an IP
+address, hostname, or path to a local socket. The service ismay be either a
+named service, or a protocol specific port. If the port is not a component of
+the address (eg, Unix domain sockets) then it should be ommitted.
+
+On plan 9, the full dial(2) dial string syntax is suported.
+
+Data Types
+----------
+
+This contains the infomation that we extract by resolving a host.
+
+ type ipaddr = union
+ `Ipv4 byte[4]
+ `Ipv6 byte[16]
+ ;;
+
+This contains an IP address. Either V4 or V6 is supported.
+
+Connections
+----------
+
+ const dial : (dialstr : byte[:] -> result(fd, byte[:]))
+
+Dial connects to a dial string as described in the summary, returning either a
+file descriptor on success, or an error description on failure. The FD
+returned is a connection to the server.
+
+ const announce : (ds : byte[:] -> result(fd, byte[:]))
+
+Announce sets up a file descriptor which is ready to listen for connections,
+and binds it to an address. Wildcards can be specified with '*' within the
+dial string.
+
+ const listen : (sock : fd -> result(fd, byte[:]))
+
+Listen specifies that the socket created with `announce` is willing to accept
+connections.
+
+ const accept : (lfd : fd -> result(fd, byte[:]))
+
+Accept takes the returned file descriptor from listen, and returns a file
+descriptor that is prepared for reading and writing.
+
+IP Parsing
+----------
+
+ const ipparse : (ip : byte[:] -> option(netaddr))
+
+Ipparse will parse an IP address. This will recognize either V4 or V6
+addresses, and `\`Some \`Ipv4 bits or `\`Some \`Ipv6 bits` as appropriate, or
+`\`None` if the address can't be parsed.
+
+ const ip4parse : (ip : byte[:] -> option(netaddr))
+
+Parses an Ipv4 address from the string `ip`. The syntax expected is dotted
+quad notation. Returns `\` Some \`Ipv4 bits` if the address parses successfully, or
+`\`None` if parsing fails.
+
+ const ip6parse : (ip : byte[:] -> option(netaddr))
+
+Parses an Ipv6 address from the string `ip`. Returns `\` Some \`Ipv4 bits` if
+the address parses successfully, or `\`None` if parsing fails. Zones are
+currently not supported. This is a bug.
+
+
+Endian flipping
+--------------
+
+ generic hosttonet : (v : @a -> @a)
+ generic nettohost : (v : @a -> @a)
+
+
+Flips the bits in an integer to match the expected. These functions should be
+avoided in favor of explicit packing functions.
+
+Examples
+--------
+
+Some simple examples of how to use the Myrddin networking API
+
+#### Echo Server
+
+ use std
+
+ const Dialstr = "tcp!*!1234"
+ const main = {
+ var lfd, afd
+ var buf : byte[1024]
+
+ match std.announce(Dialstr)
+ | `std.Ok f: lfd = f
+ | `std.Fail e: std.fatal("unable to announce on {}: {}\n", Dialstr, e)
+ ;;
+
+ match std.listen(lfd)
+ | `std.Ok f: afd = f
+ | `std.Fail e: std.fatal("unable to listen on {}: {}\n", Dialstr, e)
+ ;;
+
+ while true
+ match std.accept(afd)
+ | `std.Ok fd:
+ match std.read(fd, buf[:])
+ | `std.Ok n:
+ std.writeall(fd, buf[:n])
+ | `std.Fail e:
+ std.put("lost conection while reading: {}\n", e)
+ ;;
+ std.close(fd)
+ | `std.Fail e:
+ std.fatal("unable to accept connection on {}: {}\n", Dialstr, e)
+ ;;
+ ;;
+ }
+
+
+#### Echo Client
+
+ use std
+
+ const Dialstr = "tcp!localhost!1234"
+ const main = {args : byte[:][:]
+ var req, resp
+
+ match std.dial(Dialstr)
+ | `std.Ok fd:
+ req = std.strjoin(args[1:], " ")
+ std.writeall(fd, req)
+ resp = std.try(std.fslurp(fd))
+ std.put("{}\n", resp)
+ | `std.Fail e:
+ std.fatal("unable to dial {}: {}\n", Dialstr, e)
+ ;;
+ }
+
+Bugs
+----
+The errors returned are strings, when they should be unions with default
+formatting functions.
diff --git a/doc/api/libstd/os.txt b/doc/api/libstd/os.txt
new file mode 100644
index 0000000..2fad271
--- /dev/null
+++ b/doc/api/libstd/os.txt
@@ -0,0 +1,228 @@
+{
+ title: OS Interfaces
+ description: libstd: OS Interfaces
+}
+
+
+OS Interfaces
+-------------
+
+ pkg std =
+ type sysinfo = struct
+ system : byte[:]
+ version : byte[:]
+ release : byte[:]
+ arch : byte[:]
+ ;;
+
+ type waitstatus = union
+ `Wsuccess
+ `Wfailure
+ `Wsignalled
+ `Waiterror
+ ;;
+
+ const Enone : errno
+ const Eperm : errno
+ const Enoent : errno
+ const Esrch : errno
+ const Eintr : errno
+ const Eio : errno
+ const Enxio : errno
+ const E2big : errno
+ const Enoexec : errno
+ const Ebadf : errno
+ const Echild : errno
+ const Eagain : errno
+ const Enomem : errno
+ const Eacces : errno
+ const Efault : errno
+ const Enotblk : errno
+ const Ebusy : errno
+ const Eexist : errno
+ const Exdev : errno
+ const Enodev : errno
+ const Enotdir : errno
+ const Eisdir : errno
+ const Einval : errno
+ const Enfile : errno
+ const Emfile : errno
+ const Enotty : errno
+ const Etxtbsy : errno
+ const Efbig : errno
+ const Enospc : errno
+ const Espipe : errno
+ const Erofs : errno
+ const Emlink : errno
+ const Epipe : errno
+ const Edom : errno
+ const Erange : errno
+ const Emisc : errno
+
+ const Failmem : byte#
+
+ const getpid : ( -> pid)
+ const getsysinfo : (si : sysinfo# -> void)
+ const execvp : (cmd : byte[:], args : byte[:][:] -> int64)
+ const execvpe : (cmd : byte[:], args : byte[:][:], env : byte[:][:] -> int64)
+ const getenv : (name : byte[:] -> option(byte[:]))
+ const getenvv : (name : byte[:], default : byte[:] -> byte[:])
+ const fork : (-> pid)
+ const execv : (cmd : byte[:], args : byte[:][:] -> int64)
+ const execve : (cmd : byte[:], args : byte[:][:], env : byte[:][:] -> int64)
+ const waitpid : (pid:pid, loc:int32#, opt : int64 -> int64)
+ const spork : (cmd : byte[:][:] -> result((pid, fd, fd), int))
+ const sporkfd : (cmd : byte[:][:], infd : fd, outfd : fd -> result(pid, int))
+ const exit : (status:int -> void)
+ const now : (-> time)
+ const wait : (pid : pid -> waitstatus)
+
+ ;;
+
+ type sysinfo = struct
+ system : byte[:]
+ version : byte[:]
+ release : byte[:]
+ arch : byte[:]
+ ;;
+
+The `sysinfo` struct is returned from the operating system, giving
+some information about the sytem that this program is running on. It
+is similar to the `uname` function in the standard C library, although
+it is guaranteed to be portable. All the storage for the members is
+within the private parts of the struct, and no freeing is needed to
+relase them.
+
+ type waitstatus = union
+ `Wsuccess
+ `Wfailure
+ `Wsignalled
+ `Waiterror
+ ;;
+
+This type indicates the exit status of a child process that was invoked
+with one of the exec family of functions. It only returns a broad category
+of values, and does not return the full details provided by the os -- this
+is not portable. If the exact exit status is needed, the `sys` package
+should cover this need.
+
+ const Enone : errno
+ const Erange : errno
+ const Ebadf : errno
+ const Eexist : errno
+ const Einval : errno
+ const Efault : errno
+ const Eio : errno
+ const Emisc : errno
+
+
+The errno results are used to signal OS errors. They are not going to remain
+stable, and will probably be replaced with system call specific unions for
+error handling in future API work. Use them, but parsimoniously. The code
+that uses them will break.
+
+Emisc is used for any non-portable error codes.
+
+ const getpid : ( -> pid)
+
+Returns the process id of the current process.
+
+ const getsysinfo : (si : sysinfo# -> void)
+
+Fills a `sysinfo` struct with information about the platform that the program
+is running on.
+
+ const execv : (cmd : byte[:], args : byte[:][:] -> errno)
+ const execve : (cmd : byte[:], args : byte[:][:], env : byte[:][:] -> errno)
+
+ const execvp : (cmd : byte[:], args : byte[:][:] -> errno)
+ const execvpe : (cmd : byte[:], args : byte[:][:], env : byte[:][:] -> errno)
+
+Executes a program. If the command `cmd` begins with a `/`, then it is
+resolved as an absolute path. Otherwise, command is resolved relative to the
+current directory.
+
+If the path in `cmd` is not an absolute path, the `execvp` variants of this
+function will search the path for this program in each directory relative to
+$PATH or $path, in addition to the current directory.
+
+The arguments in `args` are passed to the executed program as its argument
+vector. By convention, the first argument in the `args` array should be the
+filename of the program being executed.
+
+For the `execvp` exec variant, the current program's environment is inherited,
+and is not modified.
+
+The `execvpe` variant of this function takes an additional argument `env`,
+which is passed to the invoked binary, replacing alll the current environment
+variables. It is in the form of a list of `"ENV_VAR=value"` key value pairs.
+
+Returns: Errno on failure. On success, the function does not return.
+
+ const getenv : (name : byte[:] -> option(byte[:]))
+
+The `getenv` function looks up a variable from the process environment.
+
+Returns: `\`Some env\_val` for an environment variable that is present in the
+environment, or `\`None` if it is not present.
+
+ const getenvv : (name : byte[:], default : byte[:] -> byte[:])
+
+The `getenvv` is the same as `getenv, with the exception that will return the
+value of the environment variable if it is present, or `default` if there is
+no such environment variable.
+
+Returns: The value of "name" if present, or "default" if not.
+
+ const fork : (-> pid)
+
+This forks a new process. This function returns twice, once in the parent
+and once in the child.
+
+On a successful fork, within the parent process `fork` returns a process ID
+greater than zero, which is the process id of the child process. Within the
+child process, `fork` returns zero.
+
+If the `fork` function returns a value less than zero, then creating a child
+process failed.
+
+Returns: The pid of the child.
+
+ const wait : (pid : pid -> waitstatus)
+
+`wait` waits for a process with the PID `pid` to exit, returning its final
+status. This function blocks until the child process has exited. If the
+process has already exited before this function is called, but has not yet
+been called on the process id of the child process, then this function
+will return a status immediately.
+
+If the child process has already been waited on, calling this function is
+invalid, and it should return a `\`Waiterror`.
+
+Returns: A waitstatus, telling you if the process crashed, exited with
+failure, exited with success, or whether the wait call was invalid.
+
+ const spork : (cmd : byte[:][:] -> result((pid, fd, fd), errno))
+
+Spork stand for `Speak to Process Fork`. It returns a process id and an
+input and output file descriptor via which you can communicate to the process
+over stdin and stdout. Stderr is inherited from the current process.
+
+Returns: Either a tuple of (pid, stdin, stdout) on success, or the error
+that caused the failure.
+
+ const sporkfd : (cmd : byte[:][:], infd : fd, outfd : fd -> result(pid, errno))
+
+Sporkfd is identical to spork, however, instead of returning new file
+descriptors, it uses the file descriptors passed in.
+
+Returns: Either the pid spawned, or the error that caused the spawn failure.
+
+
+ const exit : (status:int -> void)
+
+This exits a process with the status specified. On most Unixy systems,
+this will return the value to the shell. On Plan 9, this will call
+exits() with the number converted to a string.
+
+This function terminates the process.
diff --git a/doc/api/libstd/randomness.txt b/doc/api/libstd/randomness.txt
new file mode 100644
index 0000000..3c9934e
--- /dev/null
+++ b/doc/api/libstd/randomness.txt
@@ -0,0 +1,88 @@
+{
+ title: Randomness
+ description: libstd: Randomness
+}
+
+
+Randomness
+----------
+
+ pkg std =
+ type rng = struct
+ ...
+ ;;
+
+ generic rand : (lo : @a::(numeric,integral), hi : @a::(numeric,integral) -> @a::(numeric,integral))
+ generic randnum : (rng : rng# -> @a::(numeric,integral))
+ const randbytes : (buf : byte[:] -> size)
+
+ const mksrng : (seed : uint32 -> rng#)
+ const freerng : (rng : rng# -> void)
+ generic rngrand : (rng : rng#, lo : @a::(numeric,integral), hi : @a::(numeric,integral) -> @a::(numeric,integral))
+ generic rngrandnum : (rng : rng# -> @a::(numeric,integral))
+ const rngrandbytes : (rng : rng#, buf : byte[:] -> size)
+ ;;
+
+
+Overview
+--------
+
+Currently, the random number generation interface is quite poor. It is not
+cryptographically secure, although it should be. It exposes some functions
+that it should not.
+
+Overall, deterministic random numbers should be removed from APIs that do not
+define the specific generator.
+
+Types
+-----
+
+ type rng = struct
+ ...
+ ;;
+
+The `rng` type contains the state for the random number generator.
+
+Functions
+---------
+
+ generic rand : (lo : @a::(numeric,integral), hi : @a::(numeric,integral) -> @a::(numeric,integral))
+
+Generates a random integer in the range [lo, hi), returning the value. The
+range [lo, hi) must be positive, nonempty, and the difference between hi and
+lo must be less then 2^(type_bits - 1)
+
+ generic randnum : (rng : rng# -> @a::(numeric,integral))
+
+Generates a random integer of any magnitude the type may hold. The returned
+value may be negative, if the type is signed.
+
+ const randbytes : (buf : byte[:] -> size)
+
+Fills a buffer with random bytes.
+
+ const mksrng : (seed : uint32 -> rng#)
+
+Allocates and initializes a random number generator. The seed `seed` is used
+to seed the generator. The returned random number generator must be freed
+using `freerng`.
+
+ const freerng : (rng : rng# -> void)
+
+Frees all resources associated with the random number generator `rng`.
+
+ generic rngrand : (rng : rng#, lo : @a::(numeric,integral), hi : @a::(numeric,integral) -> @a::(numeric,integral))
+
+
+Generates a random integer from `rng` in the range [lo, hi), returning the
+value. The range [lo, hi) must be positive, nonempty, and the difference
+between hi and lo must be less then 2^(type_bits - 1)
+
+ generic rngrandnum : (rng : rng# -> @a::(numeric,integral))
+
+Generates a random integer of any size from the random number generator `rng`.
+The returned value may be negative, if the type is signed.
+
+ const rngrandbytes : (rng : rng#, buf : byte[:] -> size)
+
+Fills a buffer with random bytes from the random number generator `rng`.
diff --git a/doc/api/libstd/slices.txt b/doc/api/libstd/slices.txt
new file mode 100644
index 0000000..ca3379d
--- /dev/null
+++ b/doc/api/libstd/slices.txt
@@ -0,0 +1,71 @@
+{
+ title: Slice Manipulation
+ description: libstd: Slice Manipulation
+}
+
+
+Slice Manipulation
+------------------
+
+ pkg std =
+ generic sleq : (a : @a[:], b : @a[:] -> bool)
+ generic slcp : (dst : @a[:], src : @a[:] -> void)
+ generic slput : (sl : @a[:], idx : size, elt : @a -> @a[:])
+ generic slpush : (sl : @a[:], elt : @a -> @a[:])
+ generic slpop : (sl : @a[:] -> (@a, @a[:]))
+ generic sldup : (sl : @a[:] -> @a[:])
+ generic slfill : (sl : @a[:], v : @a -> @a[:])
+ generic sljoin : (dst : @a[:]#, src : @a[:] -> @a[:])
+ ;;
+
+
+Functions
+---------
+
+ generic sleq : (a : @a[:], b : @a[:] -> bool)
+
+Compares if two slices are equal, elementwise. Uses the '==' operator for
+each value. Returns true if they are equal, false otherwise.
+
+ generic slcp : (dst : @a[:], src : @a[:] -> void)
+
+Copies all the elements from `src` to `dst`. The copy made is shallow,
+and done using the `=` operator. The two slices must be equal size. If they
+are not equal, this will cause the program to abort.
+
+ generic slput : (sl : @a[:], idx : size, elt : @a -> @a[:])
+
+Inserts a value `elt` into the slice `sl` at index `idx`, moving all values
+from `sl[idx:len]` to `sl[idx+1:len+1]`. This assumes that the slice is either
+empty, or is allocated on the heap using `slalloc`.
+
+This may move the slice, invalidating the original input argument.
+
+ generic slpush : (sl : @a[:], elt : @a -> @a[:])
+
+Inserts a value `elt` into the slice `sl` at index the end of the slice.
+
+This may move the slice, invalidating the original input argument.
+
+
+ generic slpop : (sl : @a[:] -> (@a, @a[:]))
+
+Removes an element from the end of the slice, returning the element and the
+new, truncated slice.
+
+This may move the slice, invalidating the original input argument.
+
+ generic sldup : (sl : @a[:] -> @a[:])
+
+Duplicates a slice. This function is equivalent to calling slalloc() followed
+by slcp().
+
+ generic slfill : (sl : @a[:], v : @a -> @a[:])
+
+Fills a slice with a value.
+
+ generic sljoin : (dst : @a[:]#, src : @a[:] -> @a[:])
+
+Concatenates `src` onto the end of `dst#`. Equivalent to iterating through
+every element of `src` and `slpush()`ing it onto `dst`.
+
diff --git a/doc/api/libstd/strings.txt b/doc/api/libstd/strings.txt
new file mode 100644
index 0000000..9da210a
--- /dev/null
+++ b/doc/api/libstd/strings.txt
@@ -0,0 +1,205 @@
+{
+ title: Strings
+ description: libstd: Strings
+}
+
+Summary
+-------
+
+ pkg std =
+ /* string buffers */
+ type strbuf = struct
+ ;;
+
+ const mksb : (-> strbuf#)
+ const mkbufsb : (buf : byte[:] -> strbuf#)
+ const sbfin : (sb : strbuf# -> byte[:])
+ const sbfree : (sb : strbuf# -> void)
+ const sbpeek : (sb : strbuf# -> byte[:])
+ const sbputc : (sb : strbuf#, v : char -> bool)
+ const sbputs : (sb : strbuf#, v : byte[:] -> bool)
+ const sbputb : (sb : strbuf#, v : byte -> bool)
+ const sbtrim : (sb : strbuf#, len : size -> void)
+
+ /* string searching */
+ const strfind : (haystack : byte[:], needle : byte[:] -> option(size))
+ const strrfind : (haystack : byte[:], needle : byte[:] -> option(size))
+ const strhas : (haystack : byte[:], needle : byte[:] -> bool)
+ const hasprefix : (s : byte[:], pre : byte[:] -> bool)
+ const hassuffix : (s : byte[:], suff : byte[:] -> bool)
+
+ /* C strings */
+ const cstrlen : (buf : byte[:] -> size)
+ const cstrconv : (buf : byte[:] -> byte[:])
+ const cstrconvp : (p : byte# -> byte[:])
+
+ /* tokenizing and splitting */
+ const strsplit : (s : byte[:], delim : byte[:] -> byte[:][:])
+ const bstrsplit : (sp : byte[:][:], s : byte[:], delim : byte[:] -> byte[:][:])
+ const strtok : (s : byte[:] -> byte[:][:])
+ const bstrtok : (sp : byte[:][:], s : byte[:] -> byte[:][:])
+
+ /* string joining and stripping */
+ const strcat : (a : byte[:], b : byte[:] -> byte[:])
+ const strjoin : (strings : byte[:][:], delim : byte[:] -> byte[:])
+ const strstrip : (str : byte[:] -> byte[:])
+ const strfstrip : (str : byte[:] -> byte[:])
+ const strrstrip : (str : byte[:] -> byte[:])
+
+ /* parsing numbers out of strings */
+ generic intparsebase : (s : byte[:], base : int -> option(@a::(integral,numeric)))
+ generic intparse : (s : byte[:] -> option(@a::(integral,numeric)))
+ generic charval : (c : char, base : int -> @a::(integral,numeric))
+ ;;
+
+
+Types
+------
+
+ type strbuf = struct
+ ;;
+
+The `strbuf` type contains a string buffer under construction. It can operate
+in two modes: Allocated, and static. The allocated mode keeps track
+of buffer sizing, and grows it efficiently as the data is appended to it.
+
+The static mode, on the other hand, has a fixed size buffer that is provided
+to it, and truncates values to fit the buffer if needed. This can be used when
+allocations are undesirable.
+
+Functions: String buffers
+--------------------------
+
+ const mksb : (-> strbuf#)
+
+Mksb creates an string buffer. The buffer returned is in allocated mode, and
+starts off with an empty string.
+
+ const mkbufsb : (buf : byte[:] -> strbuf#)
+
+Mkbufsb creates a fixed size string buffer, initialized with `buf`. The
+initial length of the string is empty, regardless of the contents of the
+buffer. Anything in it will be overwritten as appends happen.
+
+ const sbfin : (sb : strbuf# -> byte[:])
+
+Sbfin finishes the string buffer. Any auxiliary resources allocated by the
+string buffer are released, and the final string that was constructed is
+returned. In dynamic mode, the string is heap allocated, and must be freed
+with `slfree`. Otherwise, it is simply a slice into the buffer passed in
+the `mkbufsb` call.
+
+ const sbfree : (sb : strbuf# -> void)
+
+Sbfree frees the string buffer `sb`, and all associated resources. The string
+under construction is discarded if the buffer is dynamically allocated.
+
+ const sbpeek : (sb : strbuf# -> byte[:])
+
+Sbpeek returns the portion of the string that has already been constructed
+by the string buffer, without freeing it. The returned string is only valid
+as long as the buffer is not modified.
+
+ const sbputc : (sb : strbuf#, v : char -> bool)
+
+Sbputc appends a single character to the string buffer, encoding it into
+utf8 before appending it to the buffer. If the buffer is fixed and the
+character will not fit, then it will be dropped in its entirety.
+
+Returns: True if there was sufficient space to append the character, false
+otherwise.
+
+ const sbputs : (sb : strbuf#, v : byte[:] -> bool)
+
+Sbputs appends a string to the string buffer. If the buffer is a static
+buffer, and the string is too long to fit, as much of it as can possibly fit
+will be copied. This may truncate characters mid-way through.
+
+Returns: True if there was sufficient space to append the character, false
+otherwise.
+
+ const sbputb : (sb : strbuf#, v : byte -> bool)
+
+Sbputs appends a single byte to the string buffer. If the buffer is a static
+buffer and the character does not fit, the buffer will remain unmodified
+
+Returns: True if there was sufficient space to append the byte, false
+otherwise.
+
+ const sbtrim : (sb : strbuf#, len : size -> void)
+
+Truncates a string buffer to the length provided. If the length provided i
+longer than the size of the buffer, the length of the buffer remains
+unmodified.
+
+
+Functions: Searching
+--------------------
+
+ const strfind : (haystack : byte[:], needle : byte[:] -> option(size))
+ const strrfind : (haystack : byte[:], needle : byte[:] -> option(size))
+
+Strfind finds the first occurrence of the string `needle` within the string
+`haystack`.Strrfind is similar, but it finds the last occurrence of `needle`
+within `haystack`.
+
+Returns: `\`std.Some index` if needle is found within haystack, or `\`None`
+otherwise.
+
+ const strhas : (haystack : byte[:], needle : byte[:] -> bool)
+
+Strhas returns `true` if the string `needle` is found within haystack.
+
+ const hasprefix : (s : byte[:], pre : byte[:] -> bool)
+
+hasprefix returns `true` if the string `pre` is found at the start of `s`.
+
+ const hassuffix : (s : byte[:], suff : byte[:] -> bool)
+
+hassuffix returns `true` if the string `pre` is found at the end of `s`.
+
+
+Functions: Splitting and joining
+--------------------------------
+
+ const strsplit : (s : byte[:], delim : byte[:] -> byte[:][:])
+ const strtok : (s : byte[:] -> byte[:][:])
+
+Strsplit and strtok will split a string into its components. Strsplit uses a
+string passed in as a delimiter, while strtok will use a variable amount of
+whitespace to split the string. They dynamically allocate the split vector,
+but not the elements within it.
+
+If there are repeated delimiters, `strsplit` will include zero length strings
+between them. For example, `std.strsplit("a<><>b<>c", "<>")` will return
+`["a", "", "b", "c"]`.
+
+ const bstrsplit : (sp : byte[:][:], s : byte[:], delim : byte[:] -> byte[:][:])
+ const strtok : (sp : byte[:][:], s : byte[:] -> byte[:][:])
+
+The bstrsplit and bstrtok functions produce a split string similar to the
+strsplit versions above, but will fill the `sp` vector passed in, instead of
+allocating their own storage. If there are more splits to be made than the
+vector can hold, then the last element will contain the tail of the string.
+
+
+ const strcat : (a : byte[:], b : byte[:] -> byte[:])
+
+Strcat will concatenate two strings, returning a new buffer containing the
+string that was created.
+
+ const strjoin : (strings : byte[:][:], delim : byte[:] -> byte[:])
+
+
+Strcat will concatenate all strings in a list, returning a new buffer
+containing the string that was created. It will interpose the delimiter
+between them.
+
+ const strstrip : (str : byte[:] -> byte[:])
+ const strfstrip : (str : byte[:] -> byte[:])
+ const strrstrip : (str : byte[:] -> byte[:])
+
+
+Strstrip will remove all whitespace characters from both ends of the string.
+Strfstrip and strrstrip will strip all whitespace characters from the start or
+end of the of the string, respectively.
diff --git a/doc/api/libstd/unicode.txt b/doc/api/libstd/unicode.txt
new file mode 100644
index 0000000..31ec714
--- /dev/null
+++ b/doc/api/libstd/unicode.txt
@@ -0,0 +1,133 @@
+{
+ title: Unicode
+ description: libstd: Unicode
+}
+
+Unicode
+--------
+
+ pkg std =
+ const Badchar : char
+ const Maxcharlen : size
+ const Maxcharval : char
+
+ /* iterators */
+ impl iterable chariter -> char
+
+ const chariter : (byte[:] -> chariter)
+
+ /* utf8 information */
+ const charlen : (chr : char -> size)
+ const encode : (buf : byte[:], chr : char -> size)
+ const decode : (buf : byte[:] -> char)
+ const strstep : (str : byte[:] -> (char, byte[:]))
+
+ /* character class predicates */
+ const isalpha : (c : char -> bool)
+ const isdigit : (c : char -> bool)
+ const isxdigit : (c : char -> bool)
+ const isnum : (c : char -> bool)
+ const isalnum : (c : char -> bool)
+ const isspace : (c : char -> bool)
+ const isblank : (c : char -> bool)
+ const islower : (c : char -> bool)
+ const isupper : (c : char -> bool)
+ const istitle : (c : char -> bool)
+
+ /* character class conversions */
+ const tolower : (c : char -> char)
+ const toupper : (c : char -> char)
+ const totitle : (c : char -> char)
+ ;;
+
+Summary
+-------
+
+As a reminder, Myrddin characters hold a single Unicode codepoint, and all
+strings are assumed to be encoded in UTF-8 by default. These functions are
+designed to facilitate manipuating unicode strings and codepoints.
+
+The APIs are generally designed that strings will be streamed through, and
+not encoded or decoded wholesale.
+
+Constants
+---------
+ const Badchar : char
+
+This is a character value that is not, and will never be, a valid unicode
+codepoint. This is generally returned when we encounter an error fr
+
+ const Maxcharlen : size
+
+This is a constant defining the maximum number of bytes that a character may
+be decoded into. It's guaranteed that a buffer that is at least Maxcharlen
+bytes long will be able to contain any character.
+
+ const Maxcharval : char
+
+This is the maximum value that any valid future unicode codepoint may decode
+into. Any character that is greater than this is an invalid character.
+
+Functions: Iterating over strings
+--------------------------------
+
+ impl iterable chariter -> char
+
+ const chariter : (byte[:] -> chariter)
+
+Chariter returns an iterator which steps through a string character by
+character.
+
+Functions: Encoding and Decoding
+--------------------------------
+
+ const charlen : (chr : char -> size)
+
+Charlen returns the length in bytes that decoding the character provided into
+unicode would take. This can vary between 1 and Maxcharlen bytes.
+
+ const encode : (buf : byte[:], chr : char -> size)
+
+Encode takes a single character, and encodes it to a utf8 string. The buffer
+must be at least long enough to hold the character.
+
+Returns: The number of bytes written, or -1 if the character could not be
+encoded.
+
+ const decode : (buf : byte[:] -> char)
+
+Decode converts the head of the buffer `buf` to a single unicode codepoint,
+returning the codepoint itself, or `Badchar` if the codepoint is invalid.
+
+The tail of the buffer is not considered, allowing this function to be used
+to peek at the contents of a string.
+
+ const strstep : (str : byte[:] -> (char, byte[:]))
+
+strstep is a function for stepping through unicode encoded strings. It
+returns the tuple (`Badchar`, str[1:]) if the value cannot be decoded,
+or `(charval, str[std.charlen(charval):])` therwise.
+
+
+```{runmyr striter}
+ s = "abcd"
+ while s.len != 0
+ (c, s) = std.striter(s)
+ std.put("next char is {}\n", s)
+ ;;
+```
+
+Character Classes
+-----------------
+
+ const isalpha : (c : char -> bool)
+ const isdigit : (c : char -> bool)
+ const isxdigit : (c : char -> bool)
+ const isnum : (c : char -> bool)
+ const isalnum : (c : char -> bool)
+ const isspace : (c : char -> bool)
+ const isblank : (c : char -> bool)
+ const islower : (c : char -> bool)
+ const isupper : (c : char -> bool)
+ const istitle : (c : char -> bool)
+
diff --git a/doc/api/libstd/varargs.txt b/doc/api/libstd/varargs.txt
new file mode 100644
index 0000000..b0eaf3e
--- /dev/null
+++ b/doc/api/libstd/varargs.txt
@@ -0,0 +1,189 @@
+{
+ title: Varargs
+ description: libstd: Varargs
+}
+
+Varargs
+-------
+
+ pkg std =
+ type typedesc = union
+ `Tynone
+
+ /* atomic types */
+ `Tyvoid
+ `Tybool
+ `Tychar
+
+ `Tyint8
+ `Tyint16
+ `Tyint
+ `Tyint32
+ `Tyint64
+
+ `Tybyte
+ `Tyuint8
+ `Tyuint16
+ `Tyuint
+ `Tyuint32
+ `Tyuint64
+ `Tyflt32
+ `Tyflt64
+ `Tyvalist
+
+ /* compound types */
+ `Typtr byte[:]
+ `Tyfunc typecursor
+ `Tyslice byte[:]
+ `Tyarray (size, byte[:])
+
+ /* aggregate types */
+ `Tytuple typecursor
+ `Tystruct typecursor
+ `Tyunion typecursor
+ /* name info */
+ `Tyname (byte[:], byte[:])
+ ;;
+
+ type typecursor = struct
+ nelt : size
+ ;;
+
+ type typeinfo = struct
+ size : size
+ align : size
+ ;;
+
+ generic typeof : (v : @a -> byte[:])
+ const typeenc : (p : ...# -> typecursor)
+ const typeenccursor : (e : byte[:] -> typecursor)
+ const typedesc : (e : byte[:] -> typedesc)
+ const typeinfo : (e : byte[:] -> typeinfo)
+ const tcnext : (t : typecursor# -> byte[:])
+ const tcpeek : (t : typecursor# -> byte[:])
+ const ncpeek : (t : typecursor# -> (byte[:], byte[:]))
+ const ncnext : (t : typecursor# -> (byte[:], byte[:]))
+
+ const vastart : (args : ...# -> valist)
+ const vatype : (ap : valist# -> byte[:])
+ const vabytes : (ap : valist# -> byte[:])
+ const vaenter : (ap : valist# -> valist)
+ generic vanext : (ap : valist# -> @a)
+ ;;
+
+Overview
+--------
+
+Type descriptions are encoded byte strings.
+
+Types
+-----
+
+
+ type typedesc = union
+ ...
+ ;;
+
+
+Typedesc provides a description of a type. It can be paired with a valist for
+walking over the contents of a value, but this is ugly.
+
+
+ type typecursor = struct
+ nelt : size
+ ;;
+
+A type cursor allows for iterating over the subtypes of a type. It exposes the
+number of elements in the subtype.
+
+ type typeinfo = struct
+ size : size
+ align : size
+ ;;
+
+Typeinfo contains all of the attributes that we care about for the type. This
+may expand in the future.
+
+
+Type iteration
+---------
+
+ generic typeof : (v : @a -> byte[:])
+
+Typeof takes any arbitrary value, and returns an encoded type description for
+the type of the value. It would be better to provide a first class interface
+for finding type encodings, but this needs thought.
+
+ const typeenc : (p : ...# -> typecursor)
+
+Typeenc returns a type cursor for an argument list, allowing for iteration
+over the type of values within it.
+
+ const typeenccursor : (e : byte[:] -> typecursor)
+
+Typeenccursor takes a type encoding, and converts it to a cursor with a single
+type. Iterating the cursor will return only the one type encoding that was
+passed to this function.
+
+ const typedesc : (e : byte[:] -> typedesc)
+
+Typedesc extracts a typedesc from an encoded type. The type description
+may contain other type cursors for iterating over subtypes.
+
+ const typeinfo : (e : byte[:] -> typeinfo)
+
+Typeinfo extracts a typeinfo from an encoded type. The type description
+contains attributes about the type, such as the size and alignment.
+
+ const tcnext : (t : typecursor# -> byte[:])
+
+Tcnext pulls an encoded subtype from a type cursor and advances it.
+Calling this on a cursor that has a name is acceptable, and will
+discard the name.
+
+ const tcpeek : (t : typecursor# -> byte[:])
+
+Tcnext pulls an encoded subtype from a type cursor and does not it.
+Calling this on a cursor that has a name is acceptable, and will
+discard the name.
+
+ const ncnext : (t : typecursor# -> (byte[:], byte[:]))
+
+Ncnext pulls an encoded subtype from a type cursor for a type with named
+subtypes, and advances the type cursor. such as a struct or union, and returns
+the name and encoded type.
+
+ const ncpeek : (t : typecursor# -> (byte[:], byte[:]))
+
+Ncpeek pulls an encoded subtype from a type cursor for a type with named
+subtypes, such as a struct or union, and returns the name and encoded
+type. This does not advance the cursor.
+
+Variadic Arguments
+-----------------
+
+ const vastart : (args : ...# -> valist)
+
+Vastart takes a pointer to a variadic argument list, and returns a valist,
+which is basically an iterator for arguments.
+
+ const vatype : (ap : valist# -> byte[:])
+
+Vatype returns a type encoding for the current variadic argument that the
+valist is pointing to.
+
+ generic vanext : (ap : valist# -> @a)
+
+Vanext returns the next value for the variadic type, and advances the valist.
+
+ const vabytes : (ap : valist# -> byte[:])
+
+Vanext returns a slice to the bytes of the variadic argument, and advances the
+valist.
+
+ const vaenter : (ap : valist# -> valist)
+
+Vaenter does not advance the valist, but returns a new valist for the
+argument, allowing iteration over the fields within the argument. For example,
+if you had a struct passed as a variadic argument, calling 'vaenter' on it
+would allow iterating over the members of the struct.
diff --git a/doc/api/libtestr/index.txt b/doc/api/libtestr/index.txt
new file mode 100644
index 0000000..774f364
--- /dev/null
+++ b/doc/api/libtestr/index.txt
@@ -0,0 +1,64 @@
+{
+ title: libtestr
+ description: Myrddin Test Library
+}
+
+Libtestr
+---------
+
+ pkg testr =
+ type ctx
+
+ type spec = struct
+ name : byte[:]
+ fn : (ctx : ctx# -> void)
+ ;;
+
+ const run : (specs : spec[:] -> void)
+ const ok : (ctx : ctx# -> void)
+ const fail : (ctx : ctx#, msg : byte[:] -> void)
+ ;;
+
+Overview
+--------
+
+The testr library provides a simple library for running
+unit tests. It outputs subtest results in a format that mbld
+will be able to understand, log, and report on.
+
+Types
+-----
+
+ type ctx
+
+The context for the current test. Used to record the state
+of the set of tests currently running.
+
+ type spec = struct
+ name : byte[:]
+ fn : (ctx : ctx# -> void)
+ ;;
+
+The specification for a single test. Contains the name
+of the test being run, and the code used to execute it.
+
+Mutex.
+
+Functions: Mutexes
+------------------
+
+ const run : (specs : spec[:] -> void)
+
+Runs a sequence of tests, recording the state of the test
+and outputting an appropriate log for mtest to consume.
+
+ const ok : (ctx : ctx# -> void)
+
+Records a test success. It does not leave the current
+scope.
+
+ const fail : (ctx : ctx#, msg : byte[:] -> void)
+
+Records a test failure. It does not leave the current
+scope.
+
diff --git a/doc/api/libthread/index.txt b/doc/api/libthread/index.txt
new file mode 100644
index 0000000..6bbe28a
--- /dev/null
+++ b/doc/api/libthread/index.txt
@@ -0,0 +1,115 @@
+{
+ title: libthread
+ description: Myrddin Thread Library
+}
+
+Libthread
+---------
+
+ pkg thread =
+ trait atomic @a::(integral,numeric) =
+ xget : (p : @a# -> @a)
+ xset : (p : @a#, v : @a -> void)
+ xadd : (p : @a#, v : @a -> @a)
+ xsub : (p : @a#, v : @a -> @a)
+ xcas : (p : @a#, old : @a, new : @a -> @a)
+ xchg : (p : @a#, new : @a -> @a)
+ ;;
+
+ type cond = struct
+ ...
+ ;;
+
+ type mutex = struct
+ ...
+ ;;
+
+ impl atomic int32
+ impl atomic int64
+ impl atomic uint32
+ impl atomic uint64
+
+ /* mutexes */
+ const mkmtx : (-> mutex)
+ const mtxlock : (mtx : mutex# -> void)
+ const mtxtrylock : (mtx : mutex# -> bool)
+ const mtxunlock : (mtx : mutex# -> void)
+
+ /* condition variables */
+ const mkcond : (mtx : mutex# -> cond)
+ const condwait : (cond : cond# -> void)
+ const condsignal : (cond : cond# -> void)
+ const condbroadcast : (cond : cond# -> void)
+ ;;
+
+Types
+-----
+
+ type cond = struct
+ ...
+ ;;
+
+Condition variable.
+
+ type mutex = struct
+ ...
+ ;;
+
+ trait atomic @a::(integral,numeric) =
+ xget : (p : @a# -> @a)
+ xset : (p : @a#, v : @a -> void)
+ xadd : (p : @a#, v : @a -> @a)
+ xsub : (p : @a#, v : @a -> @a)
+ xcas : (p : @a#, old : @a, new : @a -> @a)
+ xchg : (p : @a#, new : @a -> @a)
+ ;;
+
+
+Mutex.
+
+Functions: Mutexes
+------------------
+
+ const mkmtx : (-> mutex)
+
+Crates a new mutex, in the unlocked state.
+
+ const mtxlock : (mtx : mutex# -> void)
+
+Locks a mutex. Blocks if the mutex is already locked.
+
+ const mtxtrylock : (mtx : mutex# -> bool)
+
+Attempts to lock a mutex. Returns true if the lock was
+taken successful. Returns false if the lock was not taken.
+
+This call does not block.
+
+ const mtxunlock : (mtx : mutex# -> void)
+
+Unlocks a mutex that is taken. It is a bug to call this on a mutex that you
+have not successfully locked.
+
+Functions: Condition Variables.
+------------------------------
+
+ const mkcond : (mtx : mutex# -> cond)
+
+Creates a condition variable. Associates the mutex with the condition
+variable.
+
+ const condwait : (cond : cond# -> void)
+
+Waits on the condition to be notiifed on. Must be called with the associated
+mutex locked. Unlocks the mutex that is associated with the condition variable
+and sleeps until someone has notified on the condition variable.
+
+ const condsignal : (cond : cond# -> void)
+
+Wakes at least one waiter on the condition variable, allowing them to take the
+mutex.
+
+ const condbroadcast : (cond : cond# -> void)
+
+Wakes all waiters on the condition variable.
+