Code Planting Tutorial Part 1 - GetPoplog/Seed GitHub Wiki
One of Poplog's most powerful features is the ability to generate procedures on-the-fly using a Builder design pattern. What makes this so effective as a technique is that the "code-planting" methods are directly related to Poplog's elegant abstract machine and, behind the scenes, the code is compiled directly to machine code. The result is a very practical tradeoff between readability and efficiency.
This series of tutorials is suitable for intermediate level programmers and above. In this part it helps to know that:
- _Pop-11 end-of-line comments are written like
;;; .....
lvars x
is how Pop-11 local variables are normally declared and obey lexical binding rules.- Poplog compiles Pop-11 source code into buffers of machine code that are stored in-memory. Adding machine-code to an in-memory buffer is called 'code-planting.
- The code-planting functions all have distinctive names like
sysPOP
,sysCOMPILE
and so on.
Step 1: Getting Started
So let's get started with a very basic example. Let's use code-planting to generate a procedure that duplicates the top item on the stack. If we were to write the code in Pop-11 by hand, then we would probably write something like this:
procedure( x ); lvars x;
x, x
endprocedure;
The core of what we need looks like this - and it is fairly easy to follow.
sysPROCEDURE( false, 1 ); ;;; false means no name, 1 means it should take 1 argument.
sysLVARS( "x", 0 ); ;;; declare an lvar -x-, the 0 just means it is an ordinary identifier
sysPOP( "x" ); ;;; and pop the top value of the stack into -x-
sysPUSH( "x" ); ;;; push -x- onto the stack
sysPUSH( "x" ); ;;; and again
sysENDPROCEDURE() -> result;
Note that this isn't an ordinary Builder pattern as these code-planting methods operate on a hidden object. And we can only access this indirectly by operating inside a context-setting function called sysCOMPILE where we cause the generated code to be executed with sysEXECUTE(). So our actual code needs to be wrapped up like this:
define create_proc();
sysCOMPILE( ;;; Localises the hidden variables and calls the procedure
procedure();
sysPROCEDURE( false, 1 ); ;;; false means no name, 1 means it should take 1 argument.
sysLVARS( "x", 0 ); ;;; declare an lvar -x-
sysPOP( "x" ); ;;; and set it to the top value of the stack
sysPUSH( "x" ); ;;; push -x- onto the stack
sysPUSH( "x" ); ;;; and again
lvars p = sysENDPROCEDURE();
sysPUSHQ( p ); ;;; push the newly built procedure-record onto the stack
sysEXECUTE(); ;;; and run the code that has been generated
endprocedure
)
enddefine;
As you can see, code-planting is quite verbose but the core of it is easy enough to follow. And here's our newly created procedure doing what we wanted.
: create_proc()( 999 ) =>
** 999 999
:
Further Reading
- The full code-planting interface is described in REF VMCODE. Start with section 7 and, if that makes sense, continue onto sections 8 and 9.