# Big Moustache

*Tea for two and two for tea...*

Don't you think that the "{" character looks like a mustache? A whole lot of
them means a big moustache.

This library is a way of compiling fancy *and* fast memory accesses. The syntax
of such memory accesses is:

  <action>{<modifiers>}

"<action>" is mandatory, "<modifiers>" is optional. Default target is HAL's
[doc/hal] W register.

For example, "@{c+4}" is the equivalent of "4 + c@", but compiled in efficient
HAL.

That generated HAL is really efficient and is the main reason why Big Moustache
exists. The syntactic sugar is nice, but it alone isn't worth it. All those
moustaches are really fast.

Not all actions/modifiers combinations make sense. When they don't, their usage
lead to undefined behavior. Incompatibilities are explicitly stated in the
documentation below. The rest is compatible.

Moustaches are for compiling accesses, not interpreting them. That being said,
we make an exception for some actions, that is "@ ! @!" which can be used in
interpret mode. That's sometimes quite useful. However, interpret mode
moustaches cannot be used with modifiers "R F A P".

## Target

We call "target" the address or value that the modifiers inside a moustache
designate. By default, and like the HAL does, targets contain a single level of
indirection.

For example, "@{'data}" targets the memory at the address that the word "data"
will yield at the moment the moustache is compiled. "@{}" targets the memory
pointed by the address in the W register at runtime. "@{&}" targets the W
register directly.

Examples and equivalences:

  -{}      --> @ swap -
  *{&}     --> *
  +{'data} --> [ data litn ] @ +

### Managing W

There's a fundamental difference between W and non-W targets. Moustaches will
have a different signature depending on whether they target W or not.

For example, "@{}", which targets W and is the equivalent of a straight "@", has
the signature ( a -- n ). However, "@{R+4}", which targets RSP, has a signature
( -- n ). Similarly, "!{+4}" has a signature ( n a -- ) where "!{'foo}" has a
signature ( n -- ).

The principle is the same for all actions. See "Stack signature matrix" below
for a full list of signatures.

## Actions

Possible actions are:

  Access       @ le@ be@ ! le! be! @! @A
  Arithmetics  + - swap- * / lshift rshift and or xor = <> < > <= >=
  Special      execute

As a general rule, actions have the same stack effect as their regular Forth
word counterpart, but operand order varies. When there's a "left" operand, it
will generally be the moustache target. The right operand will either be a
number being popped from PS or a literal supplied in the moustache.

For example, the signature of the system word "-" is "( a b -- a-b )". In the
equivalent moustache action, "a" is the target and "b" is the "other" operand,
which is this case is taken from PS. Therefore, the equivalent of "-" in Big
Moustache is "swap-{&}".

It is also important to remember that moustache targets usually have a level of
indirection. For example, the signature of "-{+4}" isn't actually ( n n -- n ),
but rather ( n a -- n ) and is equivalent to "4 + @ swap -" (in efficient HAL).

### Access actions

The @ ! @! actions are straightforward and pretty much do the same thing as
their regular word counterpart.

"@{}" can't be used with "#". "@!{}" *can* be used with "#" and will have the
effect of pushing a new element to PS. For example, "@!{'foo}" has the signature
( n -- n ) while "@!{'foo #42}" has ( -- n ).

Neither of "@" "!" or "@!" can be used with the "!" or "&" modifiers.

"@A" fetches to the A register instead of the W one. Its signature is different
because it doesn't push anything to PS. See below for details.

"le@" "le!" "be@" and "be!" actions work like "@" and "!" but compile their
accesses in an endian-specific manner.

### Arithmetics actions

Arithmetic actions all follow the same logic and support all the modifiers.
They're the most "regular" of the actions.

One thing to remember is the order of the operands. The target comes first. The
plain english of an arithmetic moustache is "apply 'other' on 'target'", for
example "subtract 'other' from 'target'" or "is 'target' greater than 'other'?".
Here are some examples and their naive equivalents:

  -{&}      --> swap -
  -{&k}     --> over -
  <{&}      --> >
  -{& #42}  --> 42 swap -

### execute

The "execute" action allow indirect calls with offsets. They support only a
select few modifiers. If the target is W, a "drop" will be compiled before the
branch. It is incompatible with "# c w !" modifiers.

For example, "execute{+4}" is the equivalent of "4 + @ execute".

## Modifiers

Things become interesting when we add modifiers in the mix. Broadly speaking,
they try to expose as much of the HAL machinery as possible, but in terser
semantics.

  Target      R F P A '<val>
  Offset      +<val> -<val>
  Literal     #<val>
  Width       c w
  Flags       ! & k
  Named       ><name>

Modifiers with <val> parameters are parsed with "wordorquote", their whole
content interpreted as Forth code with the expected signature ( -- n ).

The "<name>" part of Named is read with "word" and then looked up with
"scryfind#", expecting a tagged address [doc/usage/tag] with one of the
supported tags. See "Named targets" below.

Modifiers order doesn't matter except for ">".

### Target

Target modifiers change the target of the moustache (which is W by default).

"R" is for "RSP". For example, "@{R+4}" will fetch the second element from the
RS.

"F" means "Frame". This is RSP as it was at the beginning of the word, as
tracked by [rcnt] (see below). For example, "{F-4}" is the equivalent of the
"V1" local variable (see below). "{F-16}" is equivalent to "V4". It doesn't make
sense to address "F" with a positive or null offset because it refers to the
caller's return stack, which is supposed to be opaque to us.

"P" is for "PSP". See "PSP dynamics" below.

"A" means "Address Register" (from the HAL). See paragraph below.

"'" means "Memory address". "<val>" is a hardcoded memory address. It is
important to remember that "<val>" is evaluated at compile time, with its result
being stored as a number in the moustache.

Examples:

  +{R#1}         --> r@ 1+
  +{R+4 !#1}     --> r> r> 1+ >r >r
  +{!'foo}       --> foo +!
  W@A @{+4} @{A} --> @+ swap @ swap

### Offset

Offset, in bytes, is specified by the "+" or "-" modifiers. That number of bytes
will be added to the address before the memory access is made.

### Literal

When this modifier is set, the "n" operand of the action is replaced with a
compile-time literal. For example, "-{+4 #42}" is the equivalent of
"4 + @ 42 swap -" (but much more efficient, of course).

### Type

A moustache's default type [lib/type] is DWord (native 32-bit access). You can
make a moustache's type into Byte or Word with "c" or "w". It works pretty much
exactly like "8b)" and "16b)" in the HAL. If the moustache has a "c", the access
is 8-bit, if it has a "w", it's 16-bit.

### Flags

"!" is the "store" flag. Instead of yielding the result to PS, it will store it
to the same address as specified. For example, "+{!}" is the equivalent of
"+!".

"&" is the "deref" flag. It indicates that instead of working with a source
address, we work on a value directly. For example, "lshift{&#3}" is the
equivalent of "3 lshift". Incompatible with "!".

"k" is the "keep" flag. When set the "argument" of the action (that is, the "not
the target" part of arithmetic actions or the "!" action), which always comes
from PS, is not consumed. For example, "!{k}" is the equivalent of "over swap
!". This only works on arithmetic, "!" and "@!" actions. Incompatible with "#".

### The ">" modifier

This modifier allows you to construct a moustache from another word such as a
struct field or a value. See "Named targets" below.

## Whitespaces inside moustaches

As a general rule, whitespaces aren't needed inside moustaches. However,
"wordorquote" which is used for parsing "<val>" modifiers, needs a whitespace
boundary if it's not quoted.

However, Big Moustache adds a special treatment for the last modifier allowing
it to not end with a whitespace.

This means that if you only have one "<val>" modifier, you can put it last and
thus avoid whitespaces. If you have more than one, the one that isn't the last
needs a whitespace after its "<val>" unless it's a quoted val.

Spurious spaces (spaces that aren't required as per the rules above) are
silently ignored.

Examples:

  @{+foo}
  @{+foo #bar}
  @{+"foo bar"#baz}

## Stack signature matrix

Keeping up with all possible stack effects for all possible combinations of
actions and modifiers can get dizzying, so here's a matrix to help out. "***"
means an invalid combination.

All arithmetic actions have the same signature and A/F/R/P/' targets also have
the same signature, so only "A" is represented.

         {}        {A}     {k}         {Ak}      {#}     {A#}
@        a -- n    -- n    ***         ***       ***     ***
!        n a --    n --    n a -- n    n -- n    a --    --
@!       n a -- n  n -- n  n a -- n n  n -- n n  a -- n  -- n
@A       a --      --      ***         ***       ***     ***
+        n a -- r  n -- r  n a -- n r  n -- n r  a -- n  n -- r
execute  a --      --      ***         ***       ***     ***

The store, which only makes sense in arithmetic actions, have these
signatures:

{!}    n a --
{!k}   n a -- n
{!#}   a --
{!A}   n --
{!Ak}  n -- n
{!A#}  --
{!A#k} -- n

## Working with the A register

Exposing HAL's A register is one of the great aspects making moustaches
powerful. It can decrease PS juggling a lot.

You can set the A register with "@A{}". There's also the shortcut "W@A" which
is equivalent to "@A{&}".

Then, you can use the "A" modifier in your moustaches. For exmple, let's say
that you have a pointer to a struct with fields "foo" and "bar" on PS and you'd
like to unpack that pointer to the values of foo and bar on PS. You could do
this:

  dup foo swap bar ( ptr -- foo bar )

*or* you could save yourself a dup and a swap and do:

  W@A foo @{>bar A} ( ptr -- foo bar )

It's useful to remember that the A register can serve more than once! Once
you've the A register set, you can reuse that address in a following moustache
using the "A" target. Just be aware of A lifetime rules [doc/hal], that is that
almost all words will destroy its value!

Some words do preserve it, as is explained in [doc/hal]. Look for *A* flags next
to word signatures in documentation.

Moustaches also make the promise of never touching the A register, except of
course for actions/modifiers that explicitly change it.

The S register is extensively used by moustaches and is never preserved. It is
thus not exposed by the moustache API.

## PSP dynamics

Using the "P" target can be tricky and one must be perfectly aware of PSP
dynamics before doing so. Let's use an example PS state:

  4 3 2 1

"1" is W, "2" is where PSP points to.

The {P} target works exactly like the {R} or the {A} target, that is, in this
example, a moustache {P} would target "2", {P+4} would target "3", {P+8} would
target "4".

So far, so good.

However, actions affect the stack! Now this is tricky. For example, @{P} has a
signature ( -- n ) and !{P} has the stack signature ( n -- ). What does {P}
target? (Quick answer: @{P} targets "1" and !{P} targets "2")

To answer this question, one must be aware of moustache logic when comes the
time to grow or shrink PS: growth happens before the action, shrink happens
after it.

When you think about it, it's logical: when you need to grow PS to accomodate an
action, it *has* to happen beforehand. When you need to shrink PS after having
consumed elements from it, it *has* to happen afterwards.

So, here are a few examples, again with a "4 3 2 1" PS:

Moustache   Target  Stack afterwards

@{P+4}      2       4 3 2 1 2
!{P+4}      3       4 1 2
+{P+4}      3       4 3 2 4
!{P+4 #42}  3       4 42 2 1
+{P+4 #42}  2       4 3 2 1 44
+{P+4 !}    3       4 4 2
+{P+4 !#42} 3       4 44 2 1

The "!{P+4}" moustache is particularly mind twisting, so here it is in detail:

1. The signature of the action is ( n -- ), so it's a shrink-by-1 action.
2. This means that the address where we write "n" is the PS slot where there is
   currently a "3".
3. The "n" in the action will be "1", our current top of stack.
4. We write "1" in the address where the "3" is. Stack is "4 1 2 1".
5. We shrink PS by 1. Stack is "4 1 2".

## Named targets

Instead of describing a moustache from scratch, you can also use what we call a
"named target". A named target is a scryable Forth word yielding a tagged value
of one of the supported types. This named value represent a location that is
"moustache actionable". The supported named targets are:

* values [doc/usage/to]
* field fieldw fieldb array reference [lib/struct]

Using a named target works like describing a moustache from scratch. For
example, if you have a value "foo", doing "@{>foo}" is like using the "'" target
with the address of the cell where "foo" store its value.

If you have a "struct MyStruct fields foo bar", doing "@{>bar}" is the exact
equivalent of "@{+4}".

## Local variables

It's a common pattern, to avoid PS juggling, to place an element on RS (Return
Stack) and recall that element with r@. It works well, but unfortunately, this
only works with a single element. Another drawback is that this element can be
"buried" by a pattern (a loop for example) that needs to place something on RS.
Example:

  : print100times ( n -- ) >r 100 0 do r@ . spc> loop rdrop ;

is not going to work as expected because of the loop's usage of a RS slot.
Instead, you need to do:

  : print100times ( n -- ) >r 100 0 do @{R+8} . spc> loop rdrop ;

This works but is impractical. Keeping track of RS and using different offsets
within the word to refer to the same slot results in confusing code.

A common workaround to this is to declare a "private" value near the word and
store temporary values in there.

  0 value myval
  : print100times ( n -- ) !{>myval} 100 0 do myval . spc> loop ;

It works too, but because those values are statically allocated, this can't be
done in words that require recursion. Also, it's a bit verbose.

Local variables are there to the rescue! There are 4 of them: V1 V2 V3 V4. They
work like "@{R}", but the RS elements are indexed *backwards*. For example,
let's imagine this word:

  : foo 1 >r 2 >r 3 >r 4 >r @{R+12} . 2rdrop 2rdrop ;

This snippet will print "1" because at this point, RS looks like this, stack
growing left:

  4    3    2    1    ...
  R+0  R+4  R+8  R+12

This code is tricky, because if we add or remove a RS slot, for example, if we
want to change the code above to print 10 times, it would have to look like
this:

  : foo 1 >r 2 >r 3 >r 4 >r 10 0 do @{R+20} . loop 2rdrop 2rdrop ;

because we have to know that "do" uses two RS slots. This gets confusing real
fast.

For any word, at compile time, Dusk tracks how it moves the RS with the "[rcnt]"
counter. So, most of the time, you can rely on a word to know where its RS
"bottom" is. We call this the Frame, which is the "F" moustache target. This
means that the stack can be shown thus:

  F-16 F-12 F-8  F-4  F+0
  4    3    2    1    ...
  R+0  R+4  R+8  R+12

Using that knowledge, we can do:

  : foo 1 >r 2 >r 3 >r 4 >r @{F-4} . 2rdrop 2rdrop ;
  : foo 1 >r 2 >r 3 >r 4 >r 10 0 do @{F-4} . loop 2rdrop 2rdrop ;

This will now print "1" regardless of the number of RS slots we use on top of
the "1".

Local variables are built on top of this and there are 4 of them: V1 V2 V3 V4.

  : foo 1 >r 2 >r 3 >r 4 >r V1 . 2rdrop 2rdrop ;

Unlike words using static variables, words using variables can recurse just
fine. They are also impervious to being "buried" because they remember their
"slot" at compile time and adjust their compilation accordingly.

When using local variables, you are responsible for pushing and popping to/from
RS.

More examples:

  : foo ( a b c -- ) >r >r >r V1 . spc> V2 . spc> V3 . 2rdrop rdrop ;

  1 2 3 foo \ prints "3 2 1"

  : inc ( a -- a+1 ) >r +{>V1 !#1} V1 rdrop ;
  42 inc . \ prints 43

  \ this works too
  : inc5 ( a -- a+5 ) >r 5 0 do +{>V1 !#1} loop V1 rdrop ;
  42 inc5 . \ prints 47

### Manual [rcnt] adjustments

The "R counter" that determines local variable slots at compile time is
oblivious to conditional code or loops. It's not common to have code that
conditionally maintain separate RS levels (they always need to stay balanced, of
course), but it can happen. For example, in early "exit" paths, we often have to
include a few "rdrop" before the "exit" call. If you have a fancy word that
returns a variable number of arguments, you might end up with >r and r> in
loops. All of this messes up the "R counter". You can manually adjust it through
the [rcnt] variable. For example, if you want your next ">r" to push to V1, you
would precede it with "[ 0 [rcnt] ! ]". Example case:

  : foo ( n -- )
    dup >r 42 = if rdrop ."answer to universe" exit then
    \ The conditional "rdrop" messed with [rcnt], we need manual adjustment
    [ -4 [rcnt] ! ]
    V1 . rdrop ;

## API

<action>{} *I*
  Execute or compile a moustache.

W@A *I*
  Compile a copy of the W register into the A register.

V1 V2 V3 V4
  Built-in value{}s corresponding to F-4 F-8 F-12 F-16.

## Implementations details

A moustache is a 16b struct divided in 4 fields:

+00 Flags
+04 Offset
+08 Literal
+0c Type

Flag bits are organized thus:

b2:0 base (0=W 1=R 2=A 3=' 4=F 5=P 6=S)
b5:3 reserved
b6   has literal
b7   store
b8   ref
b9   keep
b10  nosave

Offset contains the compound result of both "+" and "'" modifiers.

The literal field is only used in conjunction with the "has literal" flag.

Type is a pointer to a [lib/type] struct.

A moustache has a reset value of 0/0/0/DWord.
