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 definitions. \ 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 itself. 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 ⍵=0:(⍺-1)∇ 1 (⍺-1)∇ ⍺ ∇ ⍵-1 }⍝ ¯¯¯¯¯¯¯ 3 ack 3 61 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 61 Though used explicitly in these examples, CPS is more frequently employed behind the scenes by compiler-optimisers to transform stack-calls into tail-calls. See: http://en.wikipedia.org/wiki/Continuation-passing_style Examples: 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. OK mac'a=K (a=O a)a' ⍝ inner block defns shadow outer ones. OK mac'a=OK b=a a=KO b' ⍝ a expanded at b defn time. OK mac'a=KO b=\a a=OK b' ⍝ a expanded at b dref time. OK mac'O=? b=\\\OK b' ⍝ O not expanded. OK mac'a=K b=(a=O a) ba' ⍝ inner defn is local to block. OK ⍝ See →bfack← and →balm← for some more substantial examples. See also: bfack balm bf Back to: contents Back to: Workspaces