bfobj ← (##.mac) src                        ⍝ Simple macro processor for bf.

[mac] is a simple macro processor intended for use with →bf←.

Only  single  characters may be used as names for definitions.  For example,  we
could define ';' to generate the BF code to input a single character '0'-'9' and
to subtract '0', leaving a number 0-9 in the current cell. This version uses its
right neighbour as a temporary loop counter.


The body of a macro definition extends from the '=' character to the first white
space character (blank or newline).

A macro body may be parenthesised in order to include white space or inner local

\ Backslash prevents expansion of the immediately following character.

        mac '?=x  ?? \??\? ??'              ⍝ suppressed expansion of some ?s.
     xx ?x? xx

/ Forward slash within a macro body replicates its immediately  following  char-
  acter by the number immediately to the left of the  macro  name in the calling
  context. If no number precedes the macro name, a replication of 0 is assumed:

        mac' ∆=/+ <1∆> <2∆> <3 ∆> <4∆>'     ⍝ 1, 2, 0 and 4 +s.
     <+> <++> <3 > <++++>

Combining all of these techniques, here is a macro that outputs a single numeric
digit. It contains a local macro definition ∆:

    :=(∆=/+ >6∆[-<8∆>]< .)

Technical note:

Mac's  principal  function [mexp]'s use of sub-functions [defn] and [dref] illu-
strate a technique called "continuation-passing style" or CPS.

[mexp]  uses  [defn]  and  [dref] respectively, to define and dereference names.
Conceptually,  the  subfunctions each _return_ two lists of already- and yet-to-
be-processed tokens, with which [mexp] dyadically, and  tail-recursively,  calls

We can arrange that the calls on [defn] and [dref] are themselves tail-calls, if
we have them _call_ [mexp] rather than _return_ to it.  In other words, [mexp]'s
tail-recursive call, following evaluation of its subfunctions is devolved to the
subfunctions.  We do this by passing [mexp] as operand (∇) to the  subfunctions,
which then become suboperators:

        b≡'=':⍺ ∇ defn ⍵            ⍝ = defn: accumulate.
        a∊⊃⍺:⍺ ∇ dref ⍵             ⍝ ⍺ name: expand.
Now [defn] and [dref] can tail-call [mexp], as ⍺⍺, rather than to return to it:

        (⍺,⍨∘⊂¨name val)⍺⍺ dd uuu   ⍝ extended association vectors.
        ⍺ ⍺⍺ ddd(val(n copy)uu)     ⍝ expanded macro insert.

The same technique could be applied to the doubly-recursive Ackermann function,
whose right-most function call ⍺ ∇ ⍵-1 must return its result and so cannot be
tail-call optimised.           ¯¯¯¯¯¯¯

    ack←{ ⍝ ⎕←⎕LC
        ⍵=0:(⍺-1)∇ 1
            (⍺-1)∇ ⍺ ∇ ⍵-1
    }⍝             ¯¯¯¯¯¯¯

    3 ack 3

Passing the left-most call (⍺-1)∇ as operand to the rightmost call ⍺ ∇ ⍵-1 makes
it tail recursive:         ======                                  ¯¯¯¯¯¯¯

    ack←{ ⍝ ⎕←⎕LC
        ⍺=0:⍺⍺ ⍵+1
        ⍵=0:(⍺-1)∇ 1
        ⍺((⍺-1)∘∇)∇∇ ⍵-1
    }⍝  ¯ ======= ¯¯¯¯¯¯

    3 ⊢ack 3

Though used explicitly in these examples, CPS is more frequently employed behind
the scenes by compiler-optimisers to transform stack-calls into tail-calls.



    movl←'l=[-/<+/>] '      ⍝ move left ⍺ cell positions ("/" replicates.).

    mac movl, '2l 1l 0l'    ⍝ move left 2 1 0 (the last of which will hang BF).
[-<<+>>] [-<+>] [-+]

    mac'a=KO a=OK a'        ⍝ subsequent defns replace previous ones.
    mac'a=K (a=O a)a'       ⍝ inner block defns shadow outer ones.
    mac'a=OK b=a a=KO b'    ⍝ a expanded at b defn time.
    mac'a=KO b=\a a=OK b'   ⍝ a expanded at b dref time.
    mac'O=? b=\\\OK b'      ⍝ O not expanded.
    mac'a=K b=(a=O a) ba'   ⍝ inner defn is local to block.

⍝ See →bfack← and →balm← for some more substantial examples.

See also: bfack balm bf

Back to: contents

Back to: Workspaces