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 ] [ '<> s:format s:put ] choose ] case #2049 [ fetch-next name? [ d:lookup-xt d:name s:put ] [ '<> 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 ] [ '<> s:format s:put ] choose ] case #2049 [ fetch-next string? [ drop display:string ] if; name? [ d:lookup-xt d:name s:put ] [ '<> 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: <> fetch-next <> 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.