Monday, June 6, 2016

Tutorial : dynamic code generation in ELENA

In this tutorial we will see how to generate a new code in run-time.

ELENA does not support run-time compilation but it is possible to create group objects which will interpret their content as some kind of program. system'dynamic'Tape and system'dynamic'Struct are two array classes with custom dispatch methods to be used for such tasks.

Let's start with Tape class. It copies its content into the stack until it encounters a message or an extension and send it to the top object in the stack.

Here the simple program:

   var tape := system'dynamic'Tape(
                    "Hello World", 
                    system'console, 
                    %"writeLine[1]").
   tape eval.

The result will be:

Hello World

How it works? When eval message is sent to the tape, it starts to read its content: first it puts the literal constant into the stack, then system'console symbol. The next is a message reference - writeLine[1]. At this point the tape sends the message to the object on the stack top - system'console. The message parameter counter is 1 so "Hello World" constant is a message parameter. After the operation both objects are removed from the stack.

Let's make the situation a little bit more complex - the message parameter should be printed:

#import system'dynamic.

   ...
  
   var tape := Tape (
                   2, 
                   %"tapeOp.var[]", 
                   system'console, 
                   %"writeLine[1]").

   tape eval:"Hello again".

The result is:

Hello again

What is happening here? Before the tape starts to execute its content it puts the parameters and self variable into the stack as well. So what is in the stack before the execution is started?

   "Hello again"          <- stack top
   system'dynamic'Tape
   nil

Note that nil reference is in the stack bottom.

When the first message (in our case it is an extension - tapeOp.var[], where [] indicates an open argument list) is found, the stack looks like this:

   2                       <- stack top
   system'dynamic'Tape
   "Hello again"           
   nil

The extension tapeOp.var[] is used to access the tape stack which is considered as an open argument list. The extension target - numeric constant 2 - specifies the argument index. The equivalent plain code looks like this:

2 var &args:(self,"Hello again").

So after the extension is executed our stack looks like this:

   "Hello again"           <- stack top
   system'dynamic'Tape
   "Hello again"           
   nil

And before the final message:

   system'console          <- stack top
   "Hello again"           
   system'dynamic'Tape
   "Hello again"           
   nil

Our second group object is system'dynamic'Struct. Lets' consider a simple use case:

    var s := Struct(%x,2,%y,3).    
    var x := s x.
    var y := s y.

As you may see it is equivalent to the following code:

    var s := {  x = 2. y = 3. }
    var x := s x.
    var y := s y.

How it works? If the incoming message verb is GET (note that x and get&x are the same), the object seeks for the equivalent subject in its body and returns the following object. Otherwise it looks for the message subject, sends generic EVAL message with incoming message parameters to the next array item and returns the result.

So we may rewrite our example like this:

   var object := system'dynamic'Struct ( 
                      %print, 
                      system'dynamic'Tape ( 
                         2, 
                         %"tapeOp.var[]", 
                         system'console, 
                         %"writeLine[1]")). 
   object print:"Hello again".

Which is equivalent to the plain code:

var object := { print : s [ console writeLine:s. ] }.

In this tutorial we have learned how dynamically build and execute the code without actually compiling it.

Next tutorial - Dynamic code generation in ELENA, part 2