{cmds←''} ##.hexdump file                   ⍝ Hex dump of native file.

Native  [file]  is opened and the first few words displayed in hexadecimal. Type
<enter> to show successive words of the binary file, and ")" to quit.

Each output line starts with the file position, followed by the file content (if
any) at this position, displayed as hexadecimal 32-bit words:

      hexdump'myfile'
00000000:·020801aa 00000000 00000001 00000001·
00000010:·00000001 00000000 00000034 00000001·
00000020:·00000054 00000054 00000000 00000000·
00000030:·fffffffc 00000014 36a33e18 00000000·
00000040:·00000006 0000222f 00000001 00000003·
00000050:·ff00ff00 ffffffab 00000000 00000002·
00000060:·00000000 00000002 00000000·
00000070:·
00000080:·
00000090:·
000000a0:·)     ⍝ quit

The file position may be changed using expressions, which are evaluated left-to-
right.  The  current  value is an implicit left argument to otherwise niladic or
monadic functions. For example:

    +3      ⍝ increment.
    %4      ⍝ 4-residue.
    ⊥       ⍝ set base.

Numeric input is interpreted as hexadecimal.

Input  sequences, including partial expressions and commands, may be named using
a simple macro definition scheme.

Hexdump's optional left argument, character vector [cmds] is a sequence of com-
mands to be processed prior to interactive input.

Hexdump has a somewhat arcane user interface, reminiscent of software tools dat-
ing from the 1970s. The function could be viewed as a simple hexadecimal calcul-
ator, which just happens to display the content (if any) of its file at the pos-
ition of the current value.

Note that the current value is a 32-bit unsigned quantity, which means that only
the first 4GB of the subject file is accessible.

Expressions:

      hex   set value (file position).

    + hex   increment.
    - hex   decrement.
    × hex   multiply.
    ÷ hex   integer divide.
    % hex   integer remainder (residue commute).

    ⊥       set address base.
    !       absolute position (subtract base).
    *       get 32-bit word from file
    ← hex   put 32-bit word to file.

    : case  case selection (see below).

Commands:

    (       call, saving current state.
    )       return / quit.

    ⍝ ···   comment.
    '···'   message.
    ?       help.
    ⊤       trace on/off.
    < var   include sub-script.

    name = ···· niladic macro.
    name ⍵ = ·· monadic macro.

System variables:

    ⎕dw = · display width (words).
    ⎕be = · big-endian 0/1.

Some examples
-------------
    42                  ⍝ set file position to hex 42 (decimal 66) bytes.
    10*×4-(c*)⊥         ⍝ set base to 4 × value at 0x10, minus value at 0x0c.
    ÷100×100            ⍝ value with least significant byte cleared.
    (%2:'even':'odd')   ⍝ display parity.
    clsb = ÷100×100     ⍝ define macro.

Relative Addresses
------------------
Many binary files have an internal structure, where values in the file represent
positions relative to a "base". A Dyalog workspace, for example, contains memory
pointers  that  were  in  effect  when the workspace was saved. In the .dws file
these can be viewed as offsets from a notional base.

Command "⊥" is used to set the base, which is then added to:

    - directly  entered  hexadecimal values _excluding_ those immediately to the
      right of a monadic function:

      00000000:·2
                └────────── base added to this value.

      00000002:·+ 3 + 4
                  └───┴── base _not_ added to this value

    - values retrieved from the file content using monadic *:

      00000099:·*
                └────────── base added to value of file word at 0x99.

Apart  from  indirection  (*), the content of the file has no bearing on address
calculation. Only the current base and position play a part. To illustrate this,
the following examples use the  special file "nul", which is of zero length:

          hexdump'nul'
    00000000:·

⊥ Sets  the  base  to  the current file position. This base is then added to all
  subsequent input values to calculate the absolute file position.

    ········:·1000      ⍝ set file position to 0x1000.
    00001000:·⊥         ⍝ set base to 0x1000.
    00001000:·ab        ⍝ go to _relative_ file position 0xab,
    000010ab:·          ⍝ which is _absolute_ file position 0x10ab.

! Foils relative addressing by subtracting the current base:

    ········:·cd        ⍝ relative 0xcd
    000010cd:·!         ⍝ absolute 0xcd
    000000cd:·ef!       ⍝ absolute 0xef
    000000ef:·

To remove relative addressing, reset the base to 0:

    ········:·0!⊥       ⍝ reset base.

Call / Return
-------------
When  investigating  a tree structure, for example, it is often convenient to be
able  to return to a parent node after examining subnodes. Command "(" saves the
current state (file position, base, macro table, trace state) and it is restored
by a matching ")".

Note that saving the base allows a temporary base to be installed for the durat-
ion of a local operation.

The current calling level is indicated by the number of white dots (·) following
the input prompt. Entering ")" at level one terminates hexdump.

A nuance: there is a subtle difference between parentheses used in the above way
and those used to bind the right argument of a monadic function: 1+(2×3). In the
latter  case,  the  current  value  (6)  is _returned_ as the result of the sub-
expression,  whereas  in  the  former  (call/return) case, the pre-call value is
_restored_. Notice this distinction in the following:

          hexdump'nul'
    00000000:·2+3           ⍝ set file position to 5.
    00000005:·0''           ⍝ note file position is _5_.

    00000000:·(2+3)         ⍝ call; set file position to 5; return.
    00000000:·0''           ⍝ note file position is _0_.

    00000000:·1+(2+3)       ⍝ set file position to 1+5.
    00000006:·0''           ⍝ note file position is _6_.

    00000000:·(2+3)+1       ⍝ call; set file position to 5; return; advance 1.
    00000001:·0''           ⍝ note file position is _1_.

This rarely causes a problem in practice, as parentheses are never needed to the
left of a function, just as in APL they are never needed to the right.

Message Output
--------------
Consecutive messages are concatenated and may contain embedded newlines:

          display example       ⍝ sample script vector.
    ┌→───────┐
    │'join ' │
    │'these '│
    │'up'    │
    │        │
    │'       │
    │output  │
    │separate│
    │lines   │
    │        │
    │'       │
    └────────┘

          example hexdump'nul'
    join these up
    output
    separate
    lines

    00000000:·)

Case Selection
--------------
A case is selected using the construct (value :case0 :case1 ··· :default).

To  allow  the  selected  case to modify the environment _outside_ the construct
(for example, to set the file position) it is copied to the input stream _after_
processing the construct's closing parenthesis.

On encountering a colon:

    if the current value is zero or
    if this is the final (default) case:
        the first case is moved to immediately after the closing ")" and
        any other cases are discarded:

        ┌─┬─────────────────────────────────┐ ┌──────────────────┐
        │0│:first case:next case:··· ) more │→│) first case more │
        └─┴─────────────────────────────────┘ └──────────────────┘
        ┌─┬─────────────────────────────────┐ ┌──────────────────┐
        │n│              :final case ) more │→│) final case more │
        └─┴─────────────────────────────────┘ └──────────────────┘

    otherwise:
        the first case is discarded and
        the current value is decremented by 1:

        ┌─┬─────────────────────────────────┐ ┌───┬──────────────────────┐
        │n│:first case:next case: ···) more │→│n-1│:next case: ···) more │
        └─┴─────────────────────────────────┘ └───┴──────────────────────┘

For example:

    00000000:·('The value at 0x0c is ' c*%2 :'even':'odd')'.'
    The value at 0x0c is even.
    00000000:·123           ⍝ set value to 123.
    00000123:·$             ⍝ display current value, see $ macro below.
    123
    00000123:·(99:$)        ⍝ selects default case $, which is
    123
    00000123:·(99)$         ⍝ evaluated like this,
    123
    00000123:·(99$)         ⍝ not like this.
    99
    00000123:·

Tip:  a case may not contain an unbalanced ")", as this would terminate the con-
struct.  However, the ")" may be _named_ outside the case selection and the name
used within:

    ] = )               ⍝ ] means )
    pop = (depth::])    ⍝ pop if depth≠0.

Macros
------
File  exploration is made a little easier by being able to define simple macros.
Macro  names  may include any character and those composed exclusively from A-Z,
a-z, 0-9, _ and ⎕ may be of any length (otherwise, they are of length 1 and lex-
ically equivalent to primitive commands). This means, for example, that a macro
with a name such as ⍟, need not be blank-separated from a literal number: ⍟99.

    string      interpretation
    ------      --------------
    aabb        name "aabb"
    aa bb       two names: "aa" and "bb"
    ⍟           name "⍟"
    ⍺⍺          two names: "⍺" and "⍺"
    __          one name "__"
    ∆_⍙         three names "∆", "_" and "⍙"
    {oct}       three names "{", "oct" and "}"
    ⎕this       single name.

When a macro name is encountered in the input stream, it is immediately replaced
with  the  corresponding value and processing resumed. In particular, this means
that  the  macro value is re-scanned, so that any sub-macros are also evaluated.
Unbounded recursion will "hang" hexdump:

          hexdump'nul'      ⍝ don't try this at home:
    00000000:·hang = hang   ⍝ unboundedly recursive macro,
    00000000:·hang          ⍝ ...  hangs until interrupted.

Macros  may  redefine  (cover)  even primitive constructs. For example "!" (non-
relative value) could be coded as:

    !=-(0)  ⍝ subtract base value.

⌿ and ⍀
-------
Keystrokes <enter> and <backspace> are converted to tokens ⌿ and ⍀ respectively.
By default these system commands advance and reverse the file position by 4×⎕dw,
where  ⎕dw  is the current display width (number of word-columns). We can change
the  effect  of  entering  these  keystrokes  by  redefining them. Notice in the
following example, how backing up over the prompt, generates ⍀ commands.

          '100'hexdump'nul'
    00000100:·⌿='<er>'+1    │ advance one byte per <enter>.
    00000100:·⍀='<bs>'-1    │ back up one byte per <backspace>.
    00000100:·              │ <er>
    <er>                    │
    00000101:·              │ <er>
    <er>                    │
    00000102:               │ <bs>
    <bs>                    │
    000001                  │ <bs><bs><bs><bs>
    <bs><bs><bs><bs>        │
    000000fd:·)             │

⍀ might be coded to return from a call:

        hexdump''
    00000000:·⍀=)           │ <bs> = return from call
    00000000:·((((((        │ call to depth 7
    00000000:·······        │ <er> at depth 7
    00000010:···            │ <bs><bs><bs><bs> over 4 dots to return 4 levels
    00000000:···            │ enter at depth 3
    00000010:·              │ <bs><bs> return 2 further levels
    00000000:               │ <bs> return right out of hexdump.

NB:  ⍀ generation depends _only_ on detecting that the input is shorter than the
prompt.  If  so, a corresponding number of '⍀' chars are injected into the input
stream.  In  particular,  no  account  is  taken of the _content_ of the shorter
input. This means that chars following backspaces, up to the initial size of the
prompt, are ignored:

          hexdump''
    00000000:·((((          │ increase depth to 5.
    00000000:·····          │ <er> advances file posn 0→10.
    00000010:···1234        │ <bs><bs>1234: only 34 recognised  :-(
    00000034:·····          │ <er> advances 34→44.
    00000044:···            │ <bs><bs> reverses 44→24.
    00000024:·····1234      │ 1234 sets file posn.
    00001234:·····)))))     │ quit.

    to avoid confusion, always enter <bs> chars on a separate line. See note on
    "Prior line editing", below.

Providing  code for ⌿ can be used to great effect in annotating a structure that
extends over a number of lines of output.

NB: be wary of the effects of redefining primitive commands:

          hexdump'nul'      ⍝ don't try this at home:
    00000000:·)=            ⍝ ) expands to the null string, so
    00000000:·)             ⍝ there is no way out!
    00000000:·)
    ...

Even  hexadecimal  numbers  may  be used as macro names! When processing a token
from the input stream, hexdump looks first in the macro table _before_ wondering
whether it's a number. A hex value, occluded by a macro name, may be accessed by
prefixing an extra 0:

          hexdump'nul'
    00000000:·face          ⍝ go to 0xface.
    0000face: 0             ⍝ go to 0.
    00000000:·face='happy'  ⍝ macro definition.
    00000000:·face          ⍝ macro reference.
    happy
    00000000:·0face         ⍝ go to 0xface
    0000face:·

          hexdump'nul'
    00000000:·3='three'
    00000000:·1
    00000001:·2
    00000002:·03
    00000003:·4
    00000004:·)

The  body  of  a  macro extends as far as the next newline together, if input is
from  a  script,  with  any  following  lines that are indented further than the
defining  "=". This "offside rule" allows definitions to extend over a number of
lines and enables macros to define macros:

    aa = ...                ⍝ definition of aa, which when called
         bb = ...           ⍝   aa defines bb, which when called
              cc = ...      ⍝       bb defines cc
         dd = ...           ⍝   aa defines dd.
       ee = ...             ⍝ ee is _not_ part of aa (not right of aa's =).

Note that the above macro-defined macros are global; the only way to localise a
definition is with () call/return.

Some examples of macro definitions might be:

    right = (+4*            ⍝ call right pointer in binary tree.
    retn = )                ⍝ return from call
    line = '---------------------------------'
    help = 'm: more rows    ⍝ multi-line help output.
    ·    ·  n: next sibling
    ·    ·  q: quit'

    symb =  '········  ········ ········ ········ lft····· rgt····· val····· spc·····'
    ·    ·  lft = (+0c*
    ·    ·  rgt = (+10*
    ·    ·  val = (+14*
    ·    ·  spc = (+18*+c* nsr

    how_many = (::'one':'two':'plenty')         ⍝ primitive counting.

    quit = ) quit           ⍝ exit from any level of ")"s.

    aa = bb = cc = 'hello'  ⍝ aa defines bb, which defines cc.

Notice that indented macro definitions may be rendered more legible by the pres-
ence of "white dots" (·), which are interpreted as white space and ignored.

Macros are stored in a table, which is local to the current calling level.  This
means that macros defined after a "(", disappear with the corresponding ")".

Part of the charm, power and danger of any macro system is that incomplete expr-
essions  may  be  named; it is inherently type-unsafe. This means that erroneous
definitions may be hard to track down.

Prior line editing
------------------
It is often convenient to modify and resubmit lines previously entered into the
hexdump session:

        '⎕dw=2'hexdump ⎕wsid,'.dws'         │ Examine WS with display width = 2.
    00000000:·050903aa 80050009·1234        │ set file posn to 1234 · · · · [A]
    00001234:·00000005 00003401·            │ <er> advances 1234+8→123c
    0000123c:·037e4fb8 037e50bc·            │ <er> advances 123c+8→1244
    00000000:·050903aa 80050009·1234+4321   │ edit & reinput line [A].
    00005555:·e4000008 e8037f1c·            │ file posn is now 1234+4321.

Beware  however, that all (and only) those characters in the modified input line
to  the right of the original prompt are entered into the input stream. This can
be confusing if the display width differs between the prior and current lines:

        '⎕dw=2'hexdump ⎕wsid,'.dws'     │ Examine WS with display width = 2.
    00000000:·050903aa 80050009·'hello' │ file posn 0; output string  · · · [A]
    hello                               │
    00000000:·050903aa 80050009·⎕dw=1   │ reduce display width to 1.
    00000000:·050903aa·                 │ <er> advances 4 bytes.
    00000004:·80050009·                 │ edit and resubmit line [A]
    00000000:·050903aa 80050009·'world' │ the prompt has been overwritten
    world                               │ note that the last word of the
    80050009:·)                         │ prompt 80050009 has been input!

This  is  also the cause of the problem, mentioned above, when following a sequ-
ence of backspaces with some characters intended for input:

        '(((('hexdump''     │ start hexdump at depth 5.
    00000000:·····          │ <er> advances to posn 10.
    00000010:···1234        │ <bs><bs>1234, only '34' extends beyond prompt
    00000034:·····          │ so only 34 input.

    to avoid confusion, always enter <bs> chars on a separate line.

Writing to file
---------------
Command  "←"  overwrites  the  word  at the current position with its hex-valued
right  argument.  As a safeguard, writing is enabled only if the first character
in hexdump's left argument is a '←'.

          '←'hexdump'myfile'                                ⍝ enable write.
    00000000:·03020100 07060504 0b0a0908 0f0e0d0c·←ace
    00000000:·00000ace 07060504 0b0a0908 0f0e0d0c·(4←dead)
    00000000:·00000ace 0000dead 0b0a0908 0f0e0d0c·(8←(beef×10000))
    00000000:·00000ace 0000dead beef0000 0f0e0d0c·

To overwrite a single byte, we might use a macro to merge the new value with the
existing word:

    00000000:·00000ace 0000dead beef0000 0f0e0d0c·setb = *÷100×100+
    00000000:·00000ace 0000dead beef0000 0f0e0d0c·(f←(f setb aa))
    00000000:·00000ace 0000dead beef0000 aa0e0d0c·

Tracing
-------
Setting trace mode using "⊤" shows the current input line as each token is proc-
essed.  Addresses  generated during the trace are distinguished by a "⊤" char in
place of the ":", following the file position.

          hexdump'nul'
    00000000:·2+3×4 ''       ⍝ untraced expression.

    00000014:·⊤2+3×4⊤ ''     ⍝ traced expression.
    00000014⊤ 2+3×4⊤ ''     ⍝ traced expression.
    00000002⊤ +3×4⊤ ''     ⍝ traced expression.
    00000002⊤ 3×4⊤ ''     ⍝ traced expression.
    00000005⊤ ×4⊤ ''     ⍝ traced expression.
    00000005⊤ 4⊤ ''     ⍝ traced expression.
    00000014⊤ ⊤ ''     ⍝ traced expression.

    00000014:·2+⊤3×4⊤ ''     ⍝ traced subexpression.
    00000002⊤ 3×4⊤ ''     ⍝ traced subexpression.
    00000005⊤ ×4⊤ ''     ⍝ traced subexpression.
    00000005⊤ 4⊤ ''     ⍝ traced subexpression.
    00000014⊤ ⊤ ''     ⍝ traced subexpression.

    00000014:·

Including sub-scripts
---------------------
Command  "<"  executes the APL name to its right and inserts the result into the
input stream at that point. It would typically be used to include a subscript in
the same way as the C language preprocessor's #include statement.

In  order  for sub-scripts to take advantage of the offside rule, incoming lines
are  aligned  with  the "<" symbol in the calling script. This means that a sub-
script  may  form  part  or all of the body of a macro definition in the calling
script:

    00000000:·000901aa 00000002 00000001 00000001·<file     ⍝ include subscript.
    00000000:·000901aa 00000002 00000001 00000001·file      ⍝ show file type.
    Dyalog Component File V10.1
    00000000:·000901aa 00000002 00000001 00000001·

Coding with macros
------------------
With a little ingenuity, macros can be surprisingly flexible. For example:

          display xd            ⍝ script for hex and decimal conversion macros.
    ┌→────────────────────────────────────────────────────────────────────┐
    │⍝ display decimal equivalent of hex value:                           │
    │                                                                     │
    │    dec = (:'0':'1':'2':'3':'4':'5':'6':'7':'8':'9':(÷a dec)(%a dec))│
    │                                                                     │
    │⍝ set hex equivalent of decimal value:                               │
    │                                                                     │
    │    hex =                                                            │
    │         . = ÷10 sub × a +                                           │
    │         sub = (::(%10:.0:.1:.2:.3:.4:.5:.6:.7:.8:.9))               │
    │         sub                                                         │
    └─────────────────────────────────────────────────────────────────────┘

          xd hexdump'nul'
    00000000:·(42 dec)          ⍝ show decimal equivalent of 0x42.
    66
    00000000:·66 hex            ⍝ set hex equivalent of decimal 66.
    00000042:·)

Decode the "magic" word at the start of a Dyalog binary file:

          display file          ⍝ script for file identification macro:
    ┌→──────────────────────────────────────────────────────────┐
    │file = (0!⊥                                                │
    │·   ·   ; = '   ⍝ newline                                  │
    │·   ·       '                                              │
    │·   ·   dec = (_d:(÷a dec)(%a dec))                        │
    │·   ·   hex = (_d:'a':'b':'c':'d':'e':'f':(÷10hex)(%10hex))│
    │·   ·   ·_d = :'0':'1':'2':'3':'4':'5':'6':'7':'8':'9'     │
    │·   ·   dd  = (%100 dec) ÷100  ⍝ show dec 'n skip.         │
    │·   ·   xx  = (%100 hex) ÷100  ⍝  ..  hex  ..              │
    │·   ·                                                      │
    │·   ·   wsfile =                                           │
    │·   ·   ·   ·   'Version:  ' 2* dd '.' dd ;                │
    │·   ·   ·   ·   'Saved by: ' 4* dd '.' dd '.' dd ;         │
    │·   ·   ·   ·   'Type:     '                               │
    │·   ·   ·   ·   ·   (÷80%2:'big':'little')'-endian'        │
    │·   ·   ·   ·   ·   (÷40%2::' ,dextend')                   │
    │·   ·   ·   ·   ·   (÷20%2::' ,64-bit' )                   │
    │·   ·   ·   ·   ·   (÷10%2::' ,dalign' )                   │
    │·   ·   ·   ·   ·   (÷08%2::' ,unicode')                   │
    │·   ·                                                      │
    │·   ·   (0*%100-aa                                         │
    │·   ·   ·:'Dyalog '                                        │
    │·   ·   ·   default = 1* 'file 0x' xx                      │
    │·   ·   ·   (1*%100                                        │
    │·   ·   ·   : default                                      │
    │·   ·   ·   :'32-bit component file: ' 2* dd '.' dd        │
    │·   ·   ·   :'External Variable: '     2* dd '.' dd        │
    │·   ·   ·   :'Workspace'; wsfile                           │
    │·   ·   ·   : default                                      │
    │·   ·   ·   : default                                      │
    │·   ·   ·   : default                                      │
    │·   ·   ·   :'Session';   wsfile                           │
    │·   ·   ·   : default                                      │
    │·   ·   ·   :'64-bit component file: ' 2* dd '.' dd        │
    │·   ·   ·   : default                                      │
    │·   ·   ·   )                                              │
    │·   ·   ·:'File ' 0* xx' 'xx' 'xx' 'xx' ...'               │
    │·   ·   )                                                  │
    │·   )                                                      │
    └───────────────────────────────────────────────────────────┘

Here is the osc function (see →osc← and the example in →traj←):

          hexdump'nul'
    00000000:·osc = dec ' '(:'?'::(%2:÷2:×3+1) osc)
    00000000:·dec = (:'0':'1':'2':'3':'4':'5':'6':'7':'8':'9':(÷a dec)(%a dec))
    00000000:·3 osc
    3 10 5 16 8 4 2 1
    00000001:·7 osc
    7 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
    00000001:·)

This repeater macro calls its operand sequence a specified number of times.

00000000:·reps=(::oseq -1 reps)         ⍝ repeater macro.
00000000:·oseq='ding '                  ⍝ operand sequence.
00000000:·3 reps                        ⍝ 3 repetitions.
ding ding ding
00000000:·oseq=dec ' '                  ⍝ new operand sequence.
00000000:·5 reps                        ⍝ 5 repetitions.
5 4 3 2 1
00000000:·

Monadic macros
--------------
As  the  above  examples show, a surprising amount may be achieved using niladic
macros,  which operate on the current value. Sometimes however, it is convenient
to "parameterise" a macro with a right argument. Monadic macros are distinguish-
ed with a "⍵" between the name and the "=". Compare the niladic macro:

    ⍝ Display current value "⍺" in decimal:

        da   = (:'0':'1':'2':'3':'4':'5':'6':'7':'8':'9':(÷a da)(%a da))

with its monadic counterpart:

    ⍝ Display ⍵ in decimal:

        dw ⍵ = (⍵:'0':'1':'2':'3':'4':'5':'6':'7':'8':'9':(dw(⍵÷a))(dw(⍵%a)))
then:
    ...
    00000000:·(3e8 da)      ⍝ display "⍺"
    1000
    00000000:·dw 3e8        ⍝ display ⍵
    1000
    00000000:·
    ...

we can recode the osc function from above to take an explicit (right) argument:

        osc ⍵ = dw ⍵                ⍝ osc: probably returns 1.
        ·     · (⍵                  ⍝ arg is :
        ·     · :'?'                ⍝ 0: huh?
        ·     · :1                  ⍝ 1: done.
        ·     · :' '                ⍝ display inter-number gap.
        ·     ·  (⍵%2               ⍝ parity:
        ·     ·  : osc(⍵÷2)         ⍝ even: ∇⍵÷2
        ·     ·  : osc(⍵×3+1)       ⍝ odd:  ∇1+3×⍵
        ·     ·  )
        ·     · )
    ...
    00000000:·osc 3
    3 10 5 16 8 4 2 1
    00000001:·osc 7
    7 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
    00000001:·)

and even access both implicit (left) and explicit (right) arguments to emulate a
dyadic function. Here is Ackermann's function - but with the usual order of arg-
uments reversed, as this is easier to code. In other words: ack_ ←→ ack⍨.

        ack_ ⍵ = ⍝ da'-'dw ⍵            ⍝ (commuted) Ackermann's function.
        ·     · (⍵                      ⍝   ack_←{
        ·     · :+1 ⍝ ' '               ⍝       ⍵=0:⍺+1
        ·     · :   ⍝ ' '               ⍝
        ·     · ·(                      ⍝
        ·     · ·: 1 ack_ (⍵-1)         ⍝       ⍺=0:1 ∇ ⍵-1
        ·     · ·:-1 ack_ ⍵ ack_ (⍵-1)  ⍝       ((⍺-1)∇ ⍵)∇ ⍵-1
        ·     · ·)                      ⍝   }
        ·     · )

    ...
    00000000:·dw(0 ack_ 0)      ⍝ 0 ack⍨ 0
    1
    00000000:·dw(1 ack_ 1)      ⍝ 1 ack⍨ 1
    3
    00000000:·dw(1 ack_ 2)      ⍝ 1 ack⍨ 2
    5
    00000000:·dw(2 ack_ 3)      ⍝ 2 ack⍨ 3
    29
    00000000:·dw(3 ack_ 3)      ⍝ 3 ack⍨ 3 (takes a minute or so).
    61
    00000000:·)

(remove the ⍝ from within the first, third and forth lines to watch it working).

Finally,  this  macro displays the sequence of prime numbers, until it is inter-
rupted:

        primes = all=(prime 2::da' ')+1 all         ⍝ display primes.
        ·      · da = (:'0':'1':'2':'3':'4':'5':'6':'7':'8':'9':(÷a da)(%a da))
        ·      · prime ⍵=(-⍵:1:(%⍵:0:prime(⍵+1)))   ⍝ ⍺ is prime?
        ·      · 2 all                              ⍝ starting from 2 ...
    ...
    00000000:·primes            ⍝ display the primes.
    2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 101

Technical notes
---------------
[hexdump] maintains state variables, relatively global to the main command loop.
It  commits the sin of destructive assignment, which is generally a bad thing as
this  introduces  "state". The presence of state makes reasoning about a program
difficult  and hinders code transformation. However, in this case, the alternat-
ive  would  be  to  pass  the variables among all participating functions, which
would make the code considerably more unwieldy.

Extending Hexdump:

For  aesthetic (and pedagogic) reasons, hexdump is a pure macro processor; it is
surprising how much can be achieved with nothing other than simple text replace-
ment.  However, for serious use, coding macros would be considerably easier with
the addition of an extra primitive function or two.

For example,  if we want to implement a _stack_ in which to pass values as para-
meters, we need only add two more command lines to the loop function:

            '↓'≡cmd:rem ∇ vpush ⍵               ⍝⍝ ↓       push value.
            '↑'≡cmd:rem ∇ vpop ⍵                ⍝⍝ ↑       pop value.

together with their support functions and an initialisation line:

        vpush←{stack,⍨←⍵}                       ⍝ push value onto stack.
        vpop←{(stack↓⍨←1)⊢⊃stack,⍵}             ⍝ pop value from stack.
        stack←⍬                                 ⍝ global stack variable.

Then:

      hexdump_pro''             ⍝ extended hexdump.
00000000:·50    ⍝ go 50
00000050:·↓     ⍝ push value
00000050:·80    ⍝ go 80
00000080:·↓999  ⍝ push and go 999
00000999:·↑     ⍝ pop last value (80)
00000080:·↑     ⍝ pop previous value (50)
00000050:·↑     ⍝ empty stack: pop does nothing
00000050:·)     ⍝ quit.

and  we  could commute the (implicit) left argument with the right argument of a
monadic function mfn:

00000000:·larg ↓ rarg mfn ↑     ⍝ mfn⍨

Some handy scripts
------------------
Open an edit window on a new character vector using, for example:

    )ed →script_name

then cut & paste lines from the following:

⍝─snip─here─────────────────────────────────────────────────────────────────────

apls=(*!%100::(*!%100 apl) +1 apls)     ⍝ APL string from file.

apl=(                                   ⍝ APL char translation.
    :'⌷':'⌷':'
             '  :'⌷':' ':'':'':'':'':'	':'⌶':'ɫ':'%':'''':'⍺':'⍵'
    :'_':'a':'b':'c':'d':'e':'f':'g':'h':'i':'j':'k':'l':'m':'n':'o'
    :'p':'q':'r':'s':'t':'u':'v':'w':'x':'y':'z':'':'':'¯':'.':'⍬'
    :'0':'1':'2':'3':'4':'5':'6':'7':'8':'9':'':'⊢':'¥':'$':'£':'¢'
    :'∆':'A':'B':'C':'D':'E':'F':'G':'H':'I':'J':'K':'L':'M':'N':'O'
    :'P':'Q':'R':'S':'T':'U':'V':'W':'X':'Y':'Z':'':'':'ý':'·':''
    :'⍙':'Á':'Â':'Ã':'Ç':'È':'Ê':'Ë':'Ì':'Í':'Î':'Ï':'Ð':'Ò':'Ó':'Ô'
    :'Õ':'Ù':'Ú':'Û':'Ý':'þ':'ã':'ì':'ð':'ò':'õ':'{':'⊢':'}':'⊣':'⌷'
    :'¨':'À':'Ä':'Å':'Æ':'⍨':'É':'Ñ':'Ö':'Ø':'Ü':'ß':'à':'á':'â':'ä'
    :'å':'æ':'ç':'è':'é':'ê':'ë':'í':'î':'ï':'ñ':'[':'/':'⌿':'\':'⍀'
    :'<':'≤':'=':'≥':'>':'≠':'∨':'∧':'-':'+':'÷':'×':'?':'∊':'⍴':'~'
    :'↑':'↓':'⍳':'○':'*':'⌈':'⌊':'∇':'∘':'(':'⊂':'⊃':'∩':'∪':'⊥':'⊤'
    :'|':';':',':'⍱':'⍲':'⍒':'⍋':'⍉':'⌽':'⊖':'⍟':'⌹':'!':'⍕':'⍎':'⍫'
    :'⍪':'≡':'≢':'ó':'ô':'ö':'ø':'"':'#':'':'&':'´':'┘':'┐':'┌':'└'
    :'┼':'─':'├':'┤':'┴':'┬':'│':'@':'ù':'ú':'û':'^':'ü':'`':'∣':'¶'
    :':':'⍷':'¿':'¡':'⋄':'←':'→':'⍝':')':']':'':' ':'§':'⎕':'⍞':'⍣'
    )

⍝─snip─here─────────────────────────────────────────────────────────────────────

ascs=(*!%100::(*!%100 asc) +1 ascs)     ⍝ ASCII string from file.

asc=(                                   ⍝ ASCII char translation.
    :'⌷':'':'':'':'':'':'':'':'·':'	':'
                                             '  :'⌶':'':'·':'ɫ':'^'
    :'Ø':'É':'Ü':'À':'Ö':'Ñ':'Æ':'ø':'Ä':'Å':'í':'':'´':'∣':'':''
    :' ':'!':'"':'#':'$':'%':'&':'''':'(':')':'*':'+':',':'-':'.':'/'
    :'0':'1':'2':'3':'4':'5':'6':'7':'8':'9':':':';':'<':'=':'>':'?'
    :'@':'A':'B':'C':'D':'E':'F':'G':'H':'I':'J':'K':'L':'M':'N':'O'
    :'P':'Q':'R':'S':'T':'U':'V':'W':'X':'Y':'Z':'[':'\':']':'∧':'_'
    :'`':'a':'b':'c':'d':'e':'f':'g':'h':'i':'j':'k':'l':'m':'n':'o'
    :'p':'q':'r':'s':'t':'u':'v':'w':'x':'y':'z':'{':'|':'}':'~':''
    :'⊢':'⊣':'⊤':'⊥':'←':'→':'↑':'↓':'≤':'≥':'⍲':'⍱':'⎕':'⍞':'⌹':'⍙'
    :'⍫':'∆':'∇':'⍋':'⍒':'⍕':'⍎':'⌈':'⌊':'⍀':'⌿':'⊂':'⊃':'∩':'∪':'∨'
    :' ':'¡':'¢':'£':'⊢':'¥':'⌷':'§':'¨':'⍝':'⋄':'⍬':'≠':'≡':'⍪':'¯'
    :'∘':'○':'⌽':'⍉':'⊖':'⍟':'¶':'·':'⍺':'∊':'⍷':'≢':'⍳':'⍴':'⍵':'¿'
    :'└':'Á':'Â':'Ã':'┴':'┬':'├':'Ç':'È':'┐':'Ê':'Ë':'Ì':'Í':'Î':'Ï'
    :'Ð':'─':'Ò':'Ó':'Ô':'Õ':'┼':'×':'┘':'Ù':'Ú':'Û':'┌':'Ý':'þ':'ß'
    :'à':'á':'â':'ã':'ä':'å':'æ':'ç':'è':'é':'ê':'ë':'ì':'│':'î':'ï'
    :'ð':'ñ':'ò':'ó':'ô':'õ':'ö':'÷':'┤':'ù':'ú':'û':'ü':'ý':'⍨':'⍣'
    )

⍝─snip─here─────────────────────────────────────────────────────────────────────

⍝ display ASCII chars:

    ch4 = ⎕dw=4                     ⍝ display 4 words ASCII
        · <ascs                     ⍝ include ascii translation.
        · ch = (*!%100' 'asc) +1    ⍝ display char,
        · wd = ' 'ch ch ch ch       ⍝   ..    word,
        · '         'wd wd wd wd    ⍝   ..    line.

⍝─snip─here─────────────────────────────────────────────────────────────────────

⍝ display decimal equivalent of hex value:

    dec = (:'0':'1':'2':'3':'4':'5':'6':'7':'8':'9':(÷a dec)(%a dec))

⍝ set hex equivalent of decimal value:

    hex =
         . = ÷10 sub × a +
         sub = (::(%10:.0:.1:.2:.3:.4:.5:.6:.7:.8:.9))
         sub

⍝─snip─here─────────────────────────────────────────────────────────────────────

⍝ display current value in hex:

    $ = (:'0':'1':'2':'3':'4':'5':'6':'7':'8':'9':'a':'b':'c':'d':'e':'f':(÷10$)(%10$))

⍝─snip─here─────────────────────────────────────────────────────────────────────

Examples:
                                                                │Notes
      hexdump ⎕wsid,'.dws'                      ⍝ dump this WS  │
00000000:·050903aa 80050009 02310050 fffc3c45·                  │
00000010:·00000060 000019c0 00000000 02432f9b·'message'         │display msg.
message                                                         │
00000010:·00000060 000019c0 00000000 02432f9b·                  │
00000020:·00000000 00000000 00000000 00000000·                  │
00000030:·00000000 02432cdd 00000000 00000000·0 ''              │go 0.
                                                                │
00000000:·050903aa 80050009 02310050 fffc3c45·18c-(8*)⊥         │set base.
0000018c:·fffffff9 00000003 00003121 023141f8·⎕dw=2             │reduce width.
0000018c:·fffffff9 00000003·lft = +0c*  ⍝ follow left subtree   │define,
0000018c:·fffffff9 00000003·rgt = +10*  ⍝ follow right subtree  │   macros.
0000018c:·fffffff9 00000003·⎕dw=5                               │increase width.
0000018c:·fffffff9 00000003 00003121 023141f8 00000000·lft      │follow left.
00004334:·fffffff9 00000004 00004001 023123e8 02314d38·lft      │
00002524:·fffffff8 00000003 00000001 02315218 023152d0·rgt      │follow right.
0000540c:·fffffff9 00000003 00000001 02317210 02313a0c·rgt      │
00003b48:·fffffff9 00000003 00000001 0231546c 02315b80·?        │show help.
  hex   relative file posn.                                     │
+ hex   add.                                                    │
- hex   subtract.                                               │
× hex   multiply.                                               │
÷ hex   integer divide.                                         │
% hex   integer remainder.                                      │
*       get word from file.                                     │
← hex   put word to file.                                       │
!       absolute position.                                      │
: case  select case.                                            │
(       call.                                                   │
)       return.                                                 │
⊥       set base.                                               │
'···'   display message.                                        │
⍝       comment.                                                │
?       help.                                                   │
⊤       trace on/off.                                           │
< var   include subscript.                                      │
name=·· define macro.                                           │
00003b48:·fffffff9 00000003 00000001 0231546c 02315b80·0!⊥''    │reset base = 0.
                                                                │
00000000:·050903aa 80050009 02310050 fffc3c45 00000060·1234     │absolute addr.
00001234:·0000c801 0231c8e8 02311894 02311108 fffffffb·         │
00001248:·00000001 0000001f 00000003 00604010 fffffff8·)        │quit.

⍝ Macro definitions may be included in the left argument command stream:

      'tlx=14*' hexdump 'comp.dcf'                      ⍝ component file dump.
00000000:·000901aa 00000002 00000001 00000001·
00000010:·0000001b 00000234 00000000 00000000·tlx       'top level index'
top level index
00000234:·0000004c 000001f4 00000000 00000000·
00000244:·00000000 00000000 00000000 00000000·
00000254:·00000000 00000000 00000000 00000000·
00000264:·00000000 00000000 00000000 00000000·tlx*      'second level index'
second level index
0000004c:·00000000 00000034 0000008c 000000a4·
0000005c:·000000bc 000000d4 000000ec 00000104·
0000006c:·0000011c 00000134 0000014c 00000164·
0000007c:·0000017c 00000194 000001ac 000001c4·tlx*+4*   'first component'
first component
00000034:·0000000c 41166bcd 00000000 00000004·
0000007c:·0000017c 00000194 000001ac 000001c4·tlx*+8*   'second component'
second component
0000008c:·0000000c 41166bcd 00000000 00000004·
0000007c:·0000017c 00000194 000001ac 000001c4·tlx*+c*   'third component'
third component
000000a4:·0000000c 41166bcd 00000000 00000004·
000000b4:·0000000f 00000043 0000000c 41166bcd·)

⍝ The following vector might be used by Dyalog developers
⍝ to analyse the state indicator in an aplcore file:

      display stack
┌→──────────────────────────────────────────────────────────────────────────────────┐
│⍝ Examine aplcore stack                                                            │
│                                                                                   │
│    10*×4+c-(8*)⊥                   ⍝ Set WS bias                                  │
│                                                                                   │
│⍝ Stack                                                                            │
│                                                                                   │
│    n         =   +c*               ⍝ next frame                                   │
│    shad      = ( _shad +14         ⍝ call shadow block                            │
│    sname     = ( _sym +0*          ⍝ call shadow name                             │
│    svalue    = ( _gen +4*          ⍝ call shadow value                            │
│                                                                                   │
│⍝ Symbol                                                                           │
│                                                                                   │
│    lft   = ( _sym +c*              ⍝ call left subtree                            │
│    rgt   = ( _sym +10*             ⍝ call right subtree                           │
│    val   = ( _gen +14*             ⍝ call value                                   │
│                                                                                   │
│⍝ Cols and annotation by type:                                                     │
│                                                                                   │
│    _sym  = ⎕dw=8    'symbol:                              left     right    value'│
│    _shad = ⎕dw=3    'shadow:   sname    svalue   class'                           │
│    _gen  = ⎕dw=8                                                                  │
│                                                                                   │
│ ⎕dw=3  94!+c*                       ⍝ top of stack.                               │
└───────────────────────────────────────────────────────────────────────────────────┘

      stack hexdump 'aplcore'               ⍝ examine stack:
000e55d8:·ffffffec 00000001 00003085·n
000e5570:·ffffffe6 00000001 00004005·n
000e554c:·fffffff7 00000001 00005385·n
000e5520:·fffffff5 00000001 00005475·n
000e54a4:·fffffff9 00000001 00006005·n
000e543c:·ffffffec 00000001 00003095·n
000e53d4:·ffffffe6 00000001 00004005·shad
shadow:   sname    svalue   class
000e53e8:·02323270 00000000 00000000··
000e53f4:·02323250 00000000 00000000··
000e5400:·02311cc4 00000000 00000000··
000e540c:·02311ce4 00000000 00000000··
000e5418:·02311d44 00000000 00000000··
000e5424:·02311d24 0237c8d4 00000002··svalue
0006ca10:·fffffff8 00000005 00000627 00000001 00000003 02312084 0237c89c 0237c790···)
000e5424:·02311d24 0237c8d4 00000002··sname
symbol:                              left     right    value
00001e60:·fffffff8 00000009 00002001 02323270 02311d44 0237c89c 22202815 1d221f00···val
0006c9d8:·fffffff2 00000004 00000627 00000001 00000009 023121e4 0237c7b0 0237c7d0····(+14*
00002320:·fffffffc 00000003 0000000f 000000a6 fffffffa 00000003 0000c001 023242b8·····)))
000e5424:·02311d24 0237c8d4 00000002··
000e5430:·02311d04 0236f1b0 00000002··svalue
0005f2ec:·ffffd973 00000004 00000627 000009a2 00000004 0235a440 0235a334 0235a4b0···)
000e5430:·02311d04 0236f1b0 00000002··
000e543c:·ffffffec 00000001 00003095··)
000e53d4:·ffffffe6 00000001 00004005·n
0006cd0c:·fffffff7 00000001 00002015·n
0006ccbc:·ffffffec 00000001 00003095·n
0006cc84:·fffffff2 00000001 00004005·n
0006cc64:·fffffff8 00000001 00005455·n
0006cc4c:·fffffffa 00000001 000058a5·)

See also: hex osc traj

Back to: contents

Back to: Workspaces