Retro is a concatenative, stack based language with roots in Forth.
It is designed to be small, easily learned, and easily modified to meet specific needs, it has been developed and refined through continual use by a small community over the last decade.
This blog is written in Retro and has served as my primary means of posting things concerning Retro since 2010. The core code for Corpse is included in the Retro releases and can be freely studied and deployed.
The most recent posts are shown below. You can also view a list of all posts.
Quotations (Retro's terminology for anonymous, nestable functions) form a significant part of the Retro language. This post explores their current implementation and alternative approaches.
: foo 1 [ 2 ] dip ;
This compiles down to:
000 label: foo 001 LIT 002 1 003 CALL QUOTE 004 8 005 LIT 006 2 007 RETURN 008 CALL DIP 009 RETURN 010 RETURN
Quotes are handled by a function named quote. This is defined in the initial kernel:
t: quote ( -a ) pop, 1+, dup, @, 1-, push, 1+, ;
Or, in slightly cleaner higher-level Retro:
: quote ( -a ) pop 1+ dup @ 1- push 1+ ;
The cell following the call to quote contains the address of the cell following the end of the quote. When quote is invoked, it looks up this cell, replacing the return address with this to skip over the quote. It also leaves a pointer to the first cell in the quote on the stack.
A quote is created by [ and ends with ]. Looking at these:
t: [ ( -af ) ' quote # , here 0 # , compiler # @, compiler # on ; t: ] ( fa- ) ;; compiler # !, here over !, compiler # @, 0 # =if 1+, ; then drop, ;
Again, cleaning it up:
: [ ( -af ) "e , here 0 , @compiler compiler on ; immediate : ] ( af- ) ` ;; !compiler here over ! @compiler [ 1+ ] [ drop ] if ; immediate
[ lays down a call to quote, pushes the address of the following cell to the data stack and then lays down a dummy value. It then pushes the current compiler flag value to the stack and turns compiler on.
] lays down a return instruction using ;;, then restores compiler to the prior status. It then patches the address following the call to quote. Finally, if the compiler is now off, it leaves the address of the quote on the stack. Otherwise it discards it.
The pushing/restoring of the compiler state allows the quotes to be created at the interpreter or inside other function definitions.
To clarify the behavior at the listener level: [ acts the same way as it does in a definition. The only difference is that compiler is off at this point, so the flag pushed to the stack is 0. After this, compiler is turned on. ] will patch the destination address, and set compiler back to the flag value on the stack (which is off in this case).