Myrddin: 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.

w=WIDTH

Fill out to at least width WIDTH, filling with pad characters.

p=PAD

Fill spare width with this character. Defaults to a space character.

x

Format in hex. This is only valid for integer types.

j=joiner

Join slices with the joiner string. This leaves off the square brackets from the ends, and replaces the default joiner string ", ".

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.

This example shows how you would set up a