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.

Next Step