# Structures

The unit at [lib/struct.fs] provides an API to describe structures, that is,
data organized in terms of offsets relative to a base address. For example:

  struct Foo
    field foo
    field bar
  endstruct

describes a structure with two 32-bit fields. "foo" is at offset 0 and "bar" is
at offset 4. If a particular instance of that structure is at address $1234,
this means that the "foo" field of that instance lives at $1234 and the "bar"
field, at $1238.

## 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 category: Structure

+00 Structure category
+04 Link to structure it's extending (0 if none) (works with "reftype")
+08 size in bytes (typesz yields this)
+0c dictionary of fields

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

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. We don't use that last field to
compute struct size because we want to allow for things like "skip" or alignment
adjustments to modify the size if needed.

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.

## Defining fields

Multiple types of fields can go inside a struct definition. The most basic one
is "field", a 32-bit accessor. 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

Every time a field is added, the running offset counter for the struct being
defined goes up and that will be the offset of the next field.

There are other types of fields. For example, there are "fieldw" and "fieldb"
for defining 16-bit and 8-bit fields.

There is also "reference" that will create a field that will yield the address
of the offset. This type of field doesn't have a setter.

You might want to bump the running offset by an arbitrary amount. You can use
"skip" for this.

## 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.

However, special care must be taken for array of structures. Structure sizes
aren't automatically aligned. Example:

struct Foo
  field a
  fieldw b
endstruct

This structure, 6 bytes in size, is sound, alignment-wise. However, if you
define an array of them, you'll have alignment problems. An array-ready
structure would be defined thus:

struct Foo
  field a
  fieldw b
  2 skip
endstruct

Programming languages will use "addfield" rather than "frontend" words like
"field". "addfield" doesn't do alignment checks, which means that they need to
enforce alignment separately

## Endian-ness

This unit also provides fields for accessing memory in arbitrary endian-ness:

  fieldle fieldwle fieldbe fieldwbe

Unlike regular fields, they aren't moustaches. They are simple getters/setters
fields that only support "to" semantics.

## Methods

"method" is a field that contains the address of an execution token. As with
any field, it is expected to be called with the struct on the top of PS, and
when the method is called, the address that is contained in that field is
called, keeping the struct reference on top of PS.

Modifying the address of a method in a struct can be done with "to"
[doc/usage/to]. Examples:

  ' newvalue mydata to methodname
  : foo ['] newvalue mydata to methodname ;

Methods can't be used in absstructs.

### 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.

## Arrays

There is also arrays, which can be created with "array ( type n -- )" which
represent an array of "n" elements of the specified type. Example:

  struct Arrays
    DWord 10 array foo
    Byte 42 array bar
  endstruct

"foo" is an array of 10 32-bit numbers and "bar" is an array of 42 bytes. The
accessor field yield the address of the array.

This can be seen as an equivalent to a "reference" field followed by a "skip"
call.

## Prefixed field aliases

All fields in this unit automatically create a prefixed alias of themselves in
the form of "structname.fieldname". For example, the "Arrays" struct above
also created the words "Arrays.foo" and "Arrays.bar", which are the equivalent
of "foo" and "bar".

If you create a unit that contain generators for a new field, you have to call
"genprefixedalias ( -- )" after you've generated your field word if you want
that behavior.

## Forwarded structures

Structure types are added with "?addtype" [lib/type] which means they will
automatically try to resolve a forwarded structure. For example, this will work:

  forwardtype addtype Foo
  struct Bar
    Foo pointertype fieldt fooptr
  endstruct

  struct Foo
    ...
  endstruct

## Moustache compatibility

The fields:

  field fieldw fieldb array reference

create words that can be used as "named target" in moustaches [lib/bm]. They do
so by having their word scryable. For the first three, scrying them yields the
tag "FILD" followed by a pointer to the associated field entry (what's in the
struct dictionary), followed by a boolean 32-bit field indicating whether it
points to an absolute offset or W-relative offset.

For the other 2, scrying them yields the tag "MREF" followed by an offset
followed by the "absolute?" flag.

## Example

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

  struct Foo
    field first
    reference second 8 skip
    method third
    fieldw fourth
    fieldb fifth
    Byte 5 array sixth
  endstruct

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

## absstruct

Sometimes, you want to define a struct that isn't a base+offset, but that exists
as a single structure at an absolute address in memory. You can do this with
"absstruct":

  $1234 abstruct Struct
    field foo
    fieldw bar
    reference baz
  endstruct

  Equivalents:
  foo --> $1234 @
  bar --> $1238 w@
  baz --> $123a

## Extending a struct

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

  struct Struct1
    field foo
    field bar
  endstruct

  extends Struct1 struct Struct2
    field baz
  endstruct

  extends Struct1 struct Struct3
    field quux
  endstruct

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

## API

structtype ( 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" -- )
  Begin defining a struct

endstruct ( -- )
  Stop defining a struct

absstruct? ( -- f )
  Is the active struct a "absstruct"?

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.

field ( "name" -- )
  Define a 32-bit field. Aborts on alignment error.

fieldw ( "name" -- )
  Define a 16-bit field. Aborts on alignment error.

fieldb ( "name" -- )
  Define a 8-bit field.

fieldt ( type "name" -- )
  Define a field of specified type. "field", "fieldw" and "fieldb" are proxies
  to this word, respectively with DWord, Word and Byte arguments.

fieldtref ( type "name" -- )
  Like fieldt, but for a field that yields a reference to itself. For example,
  you'll want to use this for an embedded structure or array.

reference ( "name" -- )
  Shortcut for "Void fieltref".

array ( type n "name" -- )
  Shortcut for "swap arraytype fieldtref".

fields ( "..." -- )
  Parse the rest of line and define a 32-bit field for each word. Equivalent of
  repeatedly calling "field".

fieldst ( type "..." -- )
  Same as "fields", but repeatedly calls "fieldt" with specified type.

method ( "name" -- )
  Define a new method as per [lib/bm]'s "method{}". Aborts on alignment error.

skip ( n -- )
  Increase current struct size by n.

skipto ( n -- )
  Set current struct size to n.

  If struct is absolute, this skip is relative to the struct's base.

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