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

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

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

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

field ( "name" -- )
  Define a 32-bit field.

fieldw ( "name" -- )
  Define a 16-bit field.

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

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

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

method! ( a data "name" -- )
  Sets the address of method named "name" in struct "data" to value "a".

[method!] ( a data "name" -- ) *I*
  Immediate version of "method!".

reference ( "name" -- )
  Define a field that yields its own address.

array ( type n "name" -- )
  Define an array of n element of specified type.

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

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