One thing I'm asked about occasionally is the lack of a word like
`see`. I have no personal desire to implement a decompiler, and
don't have a need for it.

Additionally, while it sounds like it may be useful to some, this
is something that's difficult to actually implement and make accurate.
Retro runs on an emulated MISC architecture and generates code using
subroutine threading with native code inlining (STC/NCI). This leads
to complexity when trying to decompile back to source.

As a small example, let's say we want to write a quick little
decompiler. We'll use `n:inc` as a test.

    :n:inc #1 + ;

This compiles to:

    li......
    1
    ad......
    re......

I can quickly implement something like:

~~~
:token
  #1 [ fetch-next '#%n s:format s:put ] case
  #17 [ '+ s:put ] case
  'UN: s:put n:put ;

:see (a-)
  [ fetch-next [ token sp ] sip #10 eq? ] until nl drop ;
~~~

Which will work for this. But if we go a bit further we run
into a mess of calls and jumps. We can identify some of these
and expand the `token` display to handle them (and all other
non-bundled instructions):

~~~
:name? dup d:lookup-xt n:-zero? ;

:token
  #1 [ fetch-next '#%n s:format s:put ] case
  #2 [ 'dup s:put ] case
  #3 [ 'drop s:put ] case
  #4 [ 'swap s:put ] case
  #5 [ 'push s:put ] case
  #6 [ 'pop s:put ] case
  #7 [ 'jump s:put ] case
  #8 [ 'call s:put ] case
  #9 [ '\cc...... s:put ] case
  #10  [ '; s:put ] case
  #11 [ 'eq? s:put ] case
  #12 [ '-eq? s:put ] case
  #13 [ 'lt? s:put ] case
  #14 [ 'gt? s:put ] case
  #15 [ 'fetch s:put ] case
  #16 [ 'store s:put ] case
  #17 [ '+ s:put ] case
  #18 [ '- s:put ] case
  #19 [ '* s:put ] case
  #20 [ '/mod s:put ] case
  #21 [ 'and s:put ] case
  #22 [ 'or s:put ] case
  #23 [ 'xor s:put ] case
  #24 [ 'shift s:put ] case
  #25 [ '0; s:put ] case
  #26 [ '\ha...... s:put ] case
  #27 [ '\ie...... s:put ] case
  #28 [ '\iq...... s:put ] case
  #29 [ '\ii...... s:put ] case

  #1793 [ fetch-next name?
          [ d:lookup-xt d:name s:put ]
          [ '<<JUMP:%n>> s:format s:put ] choose ] case

  #2049 [ fetch-next name?
          [ d:lookup-xt d:name s:put ]
          [ '<<CALL:%n>> s:format s:put ] choose ] case

  'UN: s:put n:put
;

:see (a-)
  [ fetch-next [ token sp ] sip #10 eq? ] until nl drop ;
~~~

But this isn't enough. We need to identify embedded strings. This 
adds a bit more complexity. Strings in definitions start with a
call to `s:skip`, are followed by the string data, then a `li`teral
with the address of the first character. So this becomes:

~~~
:name? dup d:lookup-xt n:-zero? ;

:string? dup &s:skip eq? ;
:display:character dup ASCII:SPACE [ $_ c:put ] case c:put ;
:display:string $' c:put [ fetch-next display:character ] while n:inc n:inc ;

:token
  #1 [ fetch-next '#%n s:format s:put ] case
  #2 [ 'dup s:put ] case
  #3 [ 'drop s:put ] case
  #4 [ 'swap s:put ] case
  #5 [ 'push s:put ] case
  #6 [ 'pop s:put ] case
  #7 [ 'jump s:put ] case
  #8 [ 'call s:put ] case
  #9 [ '\cc...... s:put ] case
  #10  [ '; s:put ] case
  #11 [ 'eq? s:put ] case
  #12 [ '-eq? s:put ] case
  #13 [ 'lt? s:put ] case
  #14 [ 'gt? s:put ] case
  #15 [ 'fetch s:put ] case
  #16 [ 'store s:put ] case
  #17 [ '+ s:put ] case
  #18 [ '- s:put ] case
  #19 [ '* s:put ] case
  #20 [ '/mod s:put ] case
  #21 [ 'and s:put ] case
  #22 [ 'or s:put ] case
  #23 [ 'xor s:put ] case
  #24 [ 'shift s:put ] case
  #25 [ '0; s:put ] case
  #26 [ '\ha...... s:put ] case
  #27 [ '\ie...... s:put ] case
  #28 [ '\iq...... s:put ] case
  #29 [ '\ii...... s:put ] case

  #1793 [ fetch-next
          string? [ drop display:string ] if; name?
          [ d:lookup-xt d:name s:put ]
          [ '<<JUMP:%n>> s:format s:put ] choose ] case

  #2049 [ fetch-next
          string? [ drop display:string ] if; name?
          [ d:lookup-xt d:name s:put ]
          [ '<<CALL:%n>> s:format s:put ] choose ] case

  'UN: s:put n:put
;

:see (a-)
  [ fetch-next [ token sp ] sip #10 eq? ] until nl drop ;
~~~

This lets us do things like:

    :foo 'hello_world s:put nl ;
    &foo see

And get:

    'hello_world s:put nl ;

But it's still not enough. By design, Retro makes heavy use of
quotations. Each of these is effectively a word, which makes it
very difficult to determine where a word actually ends.

Consider `see`:

    :see (a-)
      [ fetch-next [ token sp ] sip #10 eq? ] until nl drop ;

Decompiling yields:

    <<JUMP:17073>> fetch-next <<JUMP:17065>> token sp ;

In this case, the first jump is to skip over the first quotation and
the second for the one embedded in that. Since decompilation stops
at the first `re`turn, most of the actual definition is lost.

It'd be possible to work around this, keeping a list of the jump
targets, and counting the embedded returns. But that still breaks
other parts, where a jump may be used for something other than
a quotation.

Working around these, it still doesn't let me reconstruct code that
uses the prefixes, or deal with inlined assembly. Or words with
hidden factors (headers hidden via the lexical scope words).

And while I can work around many (or perhaps all) of these things,
I really don't feel a need to do so. I have a functional disassembler
that can be used to verify that code is compiled correctly. And my
system is fully open: you can just read the actual code.