# Structures

This unit provides an API to describe structures, that is, data organized in
terms of offsets relative to a base address. For example:

  struct MyStruct {
    uint foo bar baz ;
    [uchar,16] somearray ; \ comments are ok
    method methodname ;
    *OtherStruct structptr ;
    +2a uint explicitoffset ;
  }

## Declaration syntax

A structure is declared with "struct" followed by the name of the structure and
an opening "{". Then, we expect an arbitrary number of field declarations until
"}" is met.

A field declaration is (square bracket means it's optional):

  [+offset] type name ... ;

The "offset" field is optional and, if present, sets the current offset to the
specified value. That offset must begin with a "+" and be followed by something
that can be parsed with "n<".

When no offset is specified, the offset of a field is that of the "current
offset", that is, a variable that starts at 0 at the beginning of a structure
declaration then increases by the size of each new field added.

The "type" part is an input string that is to be parsed by "type<" from
[lib/type].

Finally, the field name. There can be more than one name. Each name adds a new
field of the same specified type.

A field declaration ends with a ";" character.

"\" comments can be placed in between field declarations, but not "( )" ones
because they conflict with signature parsing.

## Field semantics

Each field defined in a structure adds a corresponding word of the same name to
the system dictionary. This word obeys "setter semantics" [doc/usage/to] by
consuming a data pointer on PS top and applying the correct offset before
fetching or storing. For example, if you have a field "myfield", you use it
thus:

  data myfield . \ prints the value at data+offset-of-myfield
  42 data to myfield \ sets data+offset-of-myfield to 42

For each field, a "fully qualified" dictionary entry is also added, for example
"MyStruct.myfield". Calling this word is the same as calling "myfield", but can
work around tricky shadowing issues.

## Structures as Types

A structure fills a dual role. First, it defines field words to use within Forth
semantics, but it also defines structures as types [lib/type] for introspection.
That introspection is useful in compiling contexts.

This unit defines a new type structure type with this structure:

...
+10 Link to structure it's extending (0 if none) (works with "reftype")
+14 size in bytes (typesz yields this)
+18 dictionary of fields
+1c bind pointer (0 means unbound)

The dictionary of fields is a dictionary of non-executable entries that directly
contain the field as a payload. A field is *not* a type:

+00 Link to type of field
+04 Offset of field
+08 Link to parent struct

A field always lives within an entry, which means that we can get a field name
with "xt>e entryname[]".

Last field to be added is on top of the dict.

The dictionary of fields of structure extending other structures link into their
parent's dictionary. There is nothing special to do to access all the fields.

The link to the extended structure is placed so that "reftype" works.

"typesz" on a struct yields the value of its size field.

## Alignment discipline

Dusk's alignment discipline [doc/usage/mem] dictates that structures aren't
automatically aligned, but enforces it through errors. This way, alignment is
explicit, but alignment mistakes are caught.

When using the Forth API of this unit to create structures, this is what
happens.

Structures within programming languages, even though they'll use this unit for
defining their structs, might opt for auto-aligning rather than enforcing
alignment through errors.

## Base offset

Not often, but sometimes, you'd like a struct to "start" from some arbitrary
offset other than zero. If all your fields "live" next to each other, you can
simply specify that offset in the first field's "offset" specifier.

However, if that struct is sparse and that you want to specify other offsets
manually later on, you're faced with a problem: you have to add your "start"
offset to all those manual offsets. In some cases, it's quite a noisy process.

Meet struct-level base offsets:

  struct MyStruct +$100 {
    uint foo ;
    +$2a uint bar ;
  }

When a "+" field is specified in between the struct name and the opening "{",
it will specify the structure's "base" offset.

In the structure above, field "foo" will live at offset $100 and field "bar"
will live at offsets $12a.

This offset overrides start offsets determined by "extends".

## Structure placeholders

Sometimes, structures need to reference each other and that presents us with
a chicken-and-egg problem. This problem is met with what we call a "placeholder"
system.

When declared, structures generally follow general Forth shadowing rules, that
is, if you declare a structure with a name that already exists in the
dictionary, you shadow that word.

We make one exception however. Before "struct" creates a new dictionary entry
for the specified name, it looks that name up. If that name is found, if it's a
struct, and if that struct has no field, then it is considered a placeholder
struct. In that case, instead of creating a new structure, that placeholder is
used.

Therefore, if you want to solve the chicken-and-egg problem, create an empty
structure. Example:

  struct Foo { }
  struct Bar {
    *Foo fooptr ;
  }
  struct Foo {
    ...
  }

## Extending a struct

You can pick up where another struct left off through "extends":

  struct Struct1 {
    uint foo bar ;
  }

  extends Struct1 struct Struct2 {
    uint baz ;
  }

  extends Struct1 struct Struct3 {
    uint quux
  }

Both "baz" and "quux" will have a offset +8.

## Bound structures

Structures can be bound to an *indirect* pointer so that their field word
implicitly target that pointer. This is done with "bindstruct":

  struct Struct1 {
    uint foo bar ;
  }
  variable ptr
  ptr Struct1 bindstruct

  create data1 42 , 54 ,
  create data2 43 , 55 ,
  data1 ptr !
  foo . \ prints 42
  bar . \ prints 54

  : baz foo ;
  baz . \ prints 42
  data2 ptr !
  baz . \ prints 43
  Struct1 unbindstruct
  baz . \ prints 43
  data1 bar . \ prints 54, back to regular struct semantics

When a structure is bound to a pointer, invoking one of its field word becomes
the functional equivalent to "pointer @ field" when it is unbound. Actual
generated code might be optimized to avoid push/popping to PS, but functionally
it's going to be the same.

As illustrated above, struct binding affects the field words at *compile* time,
which means that if you compile a word compiling a field word, that invocation
will be bound regardless of whether that struct is unbound later.

### Conventions around bound structures

Whether a structure is bound or not drastically changes its semantics. It's why
structures in Dusk are known as "generally bound" or "generally unbound". By
default, a structure is generally unbound. However, some structures are designed
to be generally bound. In that case, their documentation will include the
binding to indicate that fact. For example, Struct1 above would be documented
thus:

  struct Struct1 {
    uint foo bar ;
  }
  variable ptr
  ptr Struct1 bindstruct

Therefore, when using that struct, we know that it's generally bound to "ptr".

### Temporarily unbinding

While one can temporarily unbind a generally bound struct and then rebind it as
documented later, it's a bit inconvenient to do so. A more convenient
alternative is the "->" word, which forces unbound access regardless of binding
status. If we continue to use the "Struct1" example from above, we could do:

  : baz data1 -> foo ;
  baz . \ prints 42

The "->" also works when the target field belongs to an unbound struct, but in
this case it becomes a noop: the same could have been achieved by omitting the
"->" entirely.

### bindstruct and extends

When you bind a struct, structs that extend that struct keep their own binding
status. It's possible to have all those structs bound to the same indirect
pointer, but each struct has its own individual binding.

## Methods

Methods are indirect XTs where the address we want to call is contained in a
struct field. This units define two new types [lib/type]: method and smethod.

Those types are 32-bit in width and have the same setter as a uint, which means
that writing to the field allows you to change the address to be called.

On the "get" side however, these types compile a call to that address.

By conventions, methods all have a pointer to their structure as their top
argument. Static methods (smethod) don't have it.

When calling a method from an unbound structure means that a method can call its
associated XT directly (the address of the structure is already on top of PS),
but for a bound structure, it needs to place that address on top of PS before
calling it.

For static methods, it's the opposite. When calling such methods from an unbound
struct, it needs to drop PS top because the method doesn't expect it as its
argument. When calling such method from a bound structure, it can call the XT
directly.

All in all, this means that in general, generally unbound structs will contain
"method" types and generally bound structs will contains "smethod" types.

Example usage:

  struct MyStruct {
    uint foo bar ;
    method mymethod ;
    smethod staticmethod ;
  }
  : :myimpl ( mystruct -- n ) bi foo | bar + ;
  : :staticimpl ( -- n ) 123 ;
  create data 42 , 54 , ' :myimpl , ' :staticimpl ,
  data mymethod . \ prints 96
  data staticmethod . \ prints 123
  :> bi foo | bar - ; data to mymethod
  data mymethod . \ prints -12

### Method name conventions and overrides

Method implementations are rarely called directly, so their names don't matter
much. In many places, such implementations have the same name as the method, but
with a "_" prefix to indicate "private-ness".

However, sometimes structures build upon each other and method implementations
need to reference each other, mainly for overriding. Such an example is ANSIGrid
[text/ansi] which extends the grid [io/grid] and change the "writebuf" behavior,
all the while piggy-backing on its "writebuf" implementation.

Therefore, we have a convention for naming method implementations: same name as
the method, but with a ":" prefix (example: ":mymethod" for a method named
"mymethod"). This way, it's easier to predictably override a method.

## Example

Here's a full example with, below, the equivalent naive Forth code to each
accessor:

  struct Foo {
    uint first ;
    [uchar,8] second ;
    xt third ;
    ushort fourth ;
    uchar fifth ;
  }

  data first  --> data @
  data second --> data 4 +
  data third  --> data dup 12 + @ execute
  data fourth --> data 16 + w@
  data fifth  --> data 18 + c@

## API

### Constants

NULLPTR  Pointer to 0. Can be useful with "bindstruct".

### Words

newstructure ( extends -- type )
  Create and yield a new struct type that extends "extends", which can be 0.

structfields ( struct -- dict )
  Yields the dictionary of fields for this struct.

struct ( "name" -- )
  Declare a structure.

extends ( "name" -- )
  The next structure being defined will be an extension of specified struct.

containsstruct? ( struct target -- f )
  Yields whether "target" contains "struct", that is, either whether it's the
  same struct, or if struct extends, recursively, target.

findfield ( name struct -- ?type ?offset f )
  Look for field "name" in struct. If found, f=1 and type and offset correspond
  to the found field. Otherwise, f=0 and type/offset are absent.

addfield ( type "name" -- field )
  Add a field in current struct's dictionary and yield the resulting field
  struct. All "field" words below use "addfield" under the hood before they
  create their accessor word.

addalignedfield ( type "name" -- field )
  Like "addfield", but auto-aligns the struct if needed before adding the new
  one.

genprefixedalias ( -- )
  Create an alias for the latest word and add "<structurename>." in front of it.
  See "Prefixed field aliases" above.

offsetof ( "name" -- n ) *I*
  Yields the offset of the specified field whose name is on the input stream.
  When in compile mode, compile a literal with that offset.

  Aborts if name can't be found, isn't scrutable or isn't a FILD.

addrof ( "name" -- a ) *I*

  Yield the address on which the getter/setter of specified FILD/VALU/LVAR would
  act if the word was called directly or with "to".

  This word reads a word from the input stream and looks in up in scrying mode
  [usage/word], expecting it to be tagged [usage/tag]. If it's tagged with one
  of the supported tags, it yields the address where the data of that word
  lives.

  It works with those tags:

  * FILD: if struct is bound, yield target address, otherwise error
  * VALU: regular "value"
  * LVAR: V1, V2, V3, V4

  This word is an immediate that works both in compile and interpret mode. In
  interpret mode, it will yield the address directly. In compile mode, it will
  compile the indirection fetching and apply the offset on it.

bindstruct ( ptr struct -- )
  Bind "struct" to indirect "ptr". See "Bound structures" above.

unbindstruct ( struct -- )
  Unbind "struct".

-> ( -- ) *I*
  The next field access will be forced to be unbound. This sets a global flag.
  Always follow that word with a field access or else you're going to leave that
  flag dangling and it will result in miscompilation later.
