a0297491db01d4e3ed113d112556ac00eecbe787 — Virgil Dupras 13 days ago 6d37181
doc: add CC stdlib documentation

as well as :printf documentation, which was missing.
3 files changed, 116 insertions(+), 2 deletions(-)

M fs/doc/cc/index.txt
A fs/doc/cc/lib.txt
M fs/doc/io.txt
M fs/doc/cc/index.txt => fs/doc/cc/index.txt +4 -2
@@ 27,5 27,7 @@ For this reason, the core of the language is very close to ANSI C.

## Topics

* Usage (doc/cc/usage.txt)
* Implementation details (doc/cc/impl.txt)
* Usage (doc/cc/usage)
* Implementation details (doc/cc/impl)
* Standard Library (doc/cc/lib)
* DuskCC's Forth VM details (doc/cc/forth)

A fs/doc/cc/lib.txt => fs/doc/cc/lib.txt +91 -0
@@ 0,0 1,91 @@
# DuskCC standard library

While DuskCC's core language tries to stay very close to ANSI C, we allow
ourselves significant deviations from ANSI when it comes to the standard
library. Dusk's I/O is different from a regular UNIX, it handles counted strings
rather than null-terminated strings, etc. The standard library needs

The standard library lives in /comp/c/lib.fs. That library is not loaded
automatically when you load /comp/c/cc.fs. If you need that library, you should
ensure that it's loaded with `?f<< /comp/c/lib.fs`.

## Standard Library API

### Strings

unsigned char strlen(char *str)

    Return the length of string `str`.

void memset(int *addr, char c, int n)

    Fills `n` bytes, starting at address `addr`, with character `c`.

int isdigit(char c)

    Returns 1 if char is in the 0-9 range, 0 otherwise.

### I/O

The I/O API is a simple layer over the system I/O described at doc/io. `hdl` is
a regular I/O handle.

char fgetc(int hdl)                    --> IO :getc
void fputc(char c, int hdl)            --> IO :putc
void fputback(char c, int hdl)         --> IO :putback
char* freadline(int hdl)               --> IO :putback
int fopen(char *path)                  --> Path :find# + Path :open
void fread(int *addr, int n, int hdl)  --> IO :read
void fwrite(int *addr, int n, int hdl) --> IO :write
void fseek(int pos, int hdl)           --> File :seek
void fclose(int hdl)                   --> IO :close
void fputs(char *str, int hdl)         --> IO :puts
void puts(char *str)                   --> fputs(StdOut)
(see below for details about printf/scanf)
void fprintf(... char *fmt, int hdl)   --> IO :printf
void printf(... char *fmt)             --> fprintf(StdOut)
char* sprintf(... char *fmt)
int fscanf(char *fmt, int hdl)         --> see below
int scanf(char *fmt)                   --> fscanf(StdIn)
int sscanf(char *fmt, char *str)

### printf/scanf and variants

fprintf behaves a bit like the standard one, but supports less formatting
arguments and, most importantly, has a very different argument signature.

The I/O handle goes last, preceded by the formatting string, then preceded by a
variable number of arguments which must match the number of formatting
placeholder in `fmt`, *in reverse order* (this simplifies the implementation
logic a lot). For example, `printf(3, 2, 1, "%b %w %d")` prints `01 0002 3`.

printf formatting arguments are described in doc/io.

fscanf doesn't exist in sys/io yet and is only and CC stdlib. It supports only
the `%d` placeholder. Like printf, it's funky in its arguments, but in the
opposite way. It *pushes* a variable number of arguments, corresponding to the
number of placeholders in the format string, to PS. It then pushes a 1 in case
of scan success.

If it couldn't properly scan the input stream, it pushes *no* argument, only a 0
to indicate failure.

For example, `sscanf("%d %d %d", "5 6 7")` results in PS receiveing elements
`5 6 7 1`, `1` being on top. `sscanf("%d %d %d", "5 6 foo")` yields only a `0`
to PS.

printf and scanf each have `f` and `s` variants. Without a variant, printing and
scanning is done through StdOut and StdIn (see doc/io). The `f` variant allow us
to choose another target I/O handle.

For the `s` variant, we have a static buffer wrapped around a `MemFile` that is
used. The resulting string is a memory in that buffer and is overwritten at each
`sprintf` and `sscanf` call.

### Others

void qsort(unsigned int *addr, unsigned int count)

    Sorts an array of `count` integers at address `addr` using the QuickSort

M fs/doc/io.txt => fs/doc/io.txt +21 -0
@@ 134,3 134,24 @@ When loading a file to be interpreted by the system (with ":fload" or "f<<"),
those redirections are expected to stay as-is while the file is loaded. For this
reason, unless you know what you are doing, you shouldn't redirect StdIn at the
"interpret" level of a Forth source file.

## printf

An interesting augmentation of the I/O subsystem is implemented in lib/fmt.fs,
IO :printf ( ... fmt hdl -- ). It takes a format string, for example "hello %d"
which contains placeholder elements identified by the character '%' followed by
a letter.

That formatting string must be preceded by a number of arguments equal to the
number of placeholders in the string, in reverse order. Example:

    3 2 1 S" hello %d %d %d" StdOut IO :printf --> prints "hello 1 2 3"

The letter following the '%' chars determines how the argument is formatted and
these letters are supported (others produce an error):

    d - decimal
    x - 4b hexadecimal
    w - 2b hexadecimal
    b - 1b hexadecimal
    s - string