# Compiler signatures

The unit at [comp/sig.fs] is a small extension over [lib/type] that defines a
Signature type and provides convenience words to attach signatures to Forth
words, the goal being to allow compilers to interface with those words.

Once a word has been annotated with a signature, it can be called from any
language that support all the types used in that signature.

## Example usage

Most of the time, you'll simply want to annotate a word. Here's how you could,
for example, annotate the word "max ( a b -- n )":

  DWord 1 DWord DWord 2 annotate max

## Signature documentation

Many words in Dusk are annotated through this unit. When they are, the
documentation of such word will show the signature right under the Forth
signature, in this fashion:

  foobar ( a b -- c )
  uint foobar(uint b, uint a);
    Do something foobar-like.

These signatures use the C syntax, but this doesn't mean that these signatures
can only be used in C. They can be used in any language.

When a word contains a character outside the alphanumerical range, its signature
documentation will begin with a backtick:

  void `. (uint n);

This is the signature of the word "." and the backtick indicates that you're
unlikely to be able to call this word naively from your compiled language. In
[comp/c] or [comp/oberon], this means you'll have to use their backtick feature.

## Common signatures

In addition to supplying the mechanism to tag words with signatures, it also
tags some core words:

void abort();
void quit();
void noop();
void emit(uchar c);
void rtype(char* a, uint u);
void stype(char* s);
void `. (uint n);
void `.x (uint n);
void `.x2 (uint n);
void `.x1 (uint n);
void allot(uint n);
void* `allot@ (uint n);
void cfill(uint n, uint u, void *a);
void wfill(uint n, uint u, void *a);
void fill(uint n, uint u, void *a);
void cmove(uint u, void *dst, void *src);
void move(uint u, void *dst, void *src);

uint `le@ (void* a);
uint `wle@ (void* a);
void `le! (void* a, uint n);
void `wle! (void* a, uint n);
uint `be@ (void* a);
uint `wbe@ (void* a);
void `be! (void* a, uint n);
void `wbe! (void* a, uint n);

Additionally, some units that are used during Dusk bootstrapping are annotated
in the comp/sig unit rather than in those units in order to avoid having to pull
comp/sig in the bootstrap stream. Those units are:

* [lib/str]
* [io/stream]
* [fs/core]
* [drv/timer]

## API

### Signature category

The Signature category has this structure:

+00 Category
+04 Number of input elements
+08 Number of output elements
+0c Flags
... array of pointers to types for each input, then output elements.

Flags have this bit structure:

b31:1 unused
b0    Has variable arguments

The input and output fields are ordered from the shallowest (PS top) to the
deepest. For example, if we have a Forth signature "( a b -- c d )", "b" is the
first element of the "in" array and "d" is the first element of the "out" array.

Some signatures don't make sense because of the PS restriction that all
arguments are 32-bits in size. For example, an Array can't go there. You'll want
a Pointer to an Array. comp/sig doesn't validate this stuff.

"typesz" of a Signature yields 0. "type)" is a noop. Unlike Untyped, we don't
raise an error to keep the flexibility to attach a Signature (rather than a
Pointer to Signature) to a function pointer.

### Words

signaturetype ( ... n ... n -- type )
  Take two counted lists from PS where each is a list of types. The shallowest
  list is the Input signature and the deepest list is the Output. The shallowest
  item of each list represents the shallowest item of the signature.

  For example, the signature "( a b -- c d )" would go thus:

    typeC typeD 2 typeA typeB 2 signaturetype

sigcounts ( type -- inputcount outputcount )
  Yields the IN/OUT counts for specified signature.

siginputs ( type -- a )
  Yields the address of the first input type of the signature.

sigoutputs ( type -- a )
  Yields the address of the first output type of the signature.

varargs? ( type -- f )
  Yields whether the signature has the "varargs" flag set.

varargs! ( type -- )
fixedargs! ( type -- )
  Set/Unset "varargs flag".

annotate ( ... n ... n "name" -- )
  Create a signature through "signaturetype", then look for "name" in sysdict.
  Then, in [lib/tagl], tag the found execution token with "SIGT" and the created
  signature as a tag value.

annotate@ ( ... n ... n "name" -- sig )
  Same as annotate, but yield the annotated signature.

annotate< ( ... n ... n -- )
  Like annotate, but apply the same signature to all the names written on the
  rest of line.

findannotated ( name -- ?sig xt-or-0 )
  Find name in sysdict and, if the entry is tagged with "SIGT", yield the found
  xt along with the annotated signature. Otherwise, xt=0 and sig is absent.
