# Dictionary metadata

The unit [mem/dict.fs] contains a few words that are useful to play around with
dictionary structures. A lot of words revolve around dictionary metadata which
is described in doc/usage.

By "entry", we mean the address of the LL "next" field for the dictionary entry.
By "word", we mean the executable address of the entry. Remember: w>e translates
a word to an entry, e>w translates an entry into a word.

## API

Constants:

ENTRYSZ
  The maximum length of a dictionary entry, excluding the name. We say "maximum"
  because the actual number of bytes depends on alignment constraints.

Words related to dictionary:

emeta ( entry -- meta )
  Returns the metadata associated with "entry".

'emeta ( entry -- 'meta )
  Returns the address of the metadata associated with "entry".

entrylen ( entry -- len )
  Return the length of the name of "entry".

entryname[] ( entry -- a u )
  Returns the range containing "entry"'s name.

.word ( word -- )
  Print "word"'s name.

words ( -- )
  Print all word name of the dictionary.

delete ( "name" -- )
  Find "name" and makes the entry unfindeable by setting its length to 0.

forget ( "name" -- )
  Find "name" in the dictionary and bring back the sysdict to the word that
  precedes it and bring back "here" to where it was just before it was defined.
  In other words, make the system forget everything that happened since "name"
  was defined.

  WARNING: not all "forget" operations are clean. See section below.

dictlink ( "name" -- )
  Create a new "Dictionary link", as described in doc/arch, named "name".
  Whenever you want to hold an entry reference without having to dereference
  held value before passing it to "find", you should use a dictlink rather than
  a simple "create" to avoid the possibility of a false match.

Words related to metadata's linked list:

emetatype ( meta -- type )
  Return the type ID of "meta".  'emetadata ( meta -- a )
  Return the address of the data area of "meta".

?w>e ( a -- e-or-0 )
  Checks whether address "a" is the payload of a word, that is:

  1. Whether it's in "here" space or kernel space (memory can be read without
     breaking the system).
  2. Whether length byte is nonzero.
  3. Whether all characters in the name are in the range $21-7e.

  If all conditions are met, yield the associated entry. Otherwise, yield 0.

  Known limitation: this won't work for a binary-modulated "a". TODO.

## Forgetting is tricky

The forget operation is useful in many situations, but be aware of its effects
when you use it!

Not all "forget" operations are clean. In fact, many "forget" operations are
unclean. Use with care. For example, context switching [doc/lib/context] and
forgetting don't go well together. Arena memory allocation [doc/mem/alloc] and
forgetting don't go well together.

Another tricky example is "floaded,", the mechanism for remembering which files
were loaded or not, what makes ?f<< work. This is a LL where new elements are
written to "here". When you forget past the latest "floaded" point, you break
the whole chain, leading to memory corruption.

However, it's a common pattern, when working on a file, to load it, try it, then
forget it, then load it again and so on. This will work, but be aware of these
facts:

1. The "floaded" node is written to "here" *before* the file is actually loaded.
2. "f<<" does not add a node if the node is already present.
3. The "floaded" LL goes backwards ("floaded" is a value that points to LL's
   head)

Therefore, you can have a "f<< / forget" loop if you always load the same single
file. If you really need to load multiple files, save the value of "floaded"
somewhere before you load your multiple paths. Than, right after your forget
(before you load any file), restore "floaded" back to that value.
