Wednesday, February 1, 2012

Dynamic Programming in ELENA

Last year I demonstrated how to create BF interpreter without writing any program. But to implement a loop I still had to create a new class (in runtime). After a while I realize that it is possible to get rid of auto code generation completely, using only special group types (except for dynamic subjects).
Let's start with general overview of dynamic programming paradigm in ELENA. Instead of writing a code (with the help of run-time compilation or some built-in functionality) we could build (assemble) a new object reusing existing functionality. Subjects and built-in verb roles will allow us to create new methods and invoke them (without any kind of reflection mechanisms), different group objects (wrap, echo, cast, prop and action) will combine them to implement the program logic.
Presume we would like to create a dynamic code which will execute the following bf code: , [ . , ]
First of all we have to create a tape:
anIndexer := factory'NewArray::{ 
    &factory'array_size:1024 
    &factory'pattern:(WideCharVar::0) 
} @ 0.
Now we have to add a new dynamic method to the indexer which executes the loop until the current item is zero. So we create a group and add the indexer into it:
#var aGroup := sys'dynamics'GroupVariable 
                append &sys'dynamics'group_member: anIndexer
Our dynamic method will need its own subject:
#subject bfloop.
Let's create the method:
aGroup append &sys'dynamics'group_member:
   __prop(bfloop, __run, __wrap(EWhileNotZero, aGroup)).
In this code we use two types of group objects:__prop and __wrap. __prop is a property collection which consists of a subject symbol (bfloop), a wrapper (verb external role) and a content - another wrapper. The property converts a qualified message (bfloop'invoke) into a generic one (invoke) and sends it to a verb role. A verb role in its turn sends the appropriate message (run) to the property content (__wrap(EWhileNotZero, aGroup)). The created group is equivalent to the following code:
aGroup append &sys'dynamics'group_member: 
{ 
    bfloop'invoke : anAction = self~EWhileNotZero run:anAction. 
}.
To implement input / output operations we will use the following actions:
#var anOutputAction := __wrap(__write, 'program'output).
#var anInputAction := __wrap(__save, __action(__get, 'program'input, nil)).
The "traditional" code looks like this:
#var anOutputAction :={ invoke : aGroup = 'program'output write:aGroup. }.
#var anInputAction :={ invoke : aGroup = 'program'input get save:aGroup. }.
Note that once again we use verb roles to translate invoke into write and save respectively. anInputAction is a bit more complicated. We need to implement the action before invoking save message. That's why another special group - __action - is used.
And finally we combine these actions:
__cast(anInputAction,
    __echo(bfloop, 
         __wrap(__eval, __cast(anOutputAction, anInputAction)))
) invoke:aGroup.
Which could be translated into the following code:
__cast(anInputAction, 
{ 
   invoke : aGroup =  
      aGroup invoke &bfloop:__cast(anOutputAction, anInputAction) 
}) invoke:aGroup.
As you can see we were able to create an object with a custom method without actually declaring a new class. Of course this approach is not suited for programmers but it could be used for automatic code assembling inside the program in run-time, for example in ELENAVM script:
tape       =>
              // 1: append
              &subject 
              // 2: goto
              &subject
              // 3: loop
              &subject
              // 4: inc action
              &1 1 &echo
              // 5: dec action
              &1 -1 &echo
              // 6: next action
              &2 1 &echo
              // 7: prev action
              &2 -1 &echo
              // 8: output action
              &__write &nil 'program'output &wrap
              // 9: group
              &nil
              sys'dynamics'groupvariable
              &std'basic'factory'array_size &sys'vm'routines'egetprop 1024 &prop
              &std'basic'factory'pattern &sys'vm'routines'egetprop 0 std'basic'widecharvar &prop
              &union2
              std'basic'factory'patternarrayfactory
              std'basic'factory'newarray 
              0 ^refer 
              ~ sys'dynamics'group_member ^append
              // :: append method
              &1 &__append &9 &prop
              ~ sys'dynamics'group_member ^append
              // :: goto method
              &2 &__append &std'dictionary'index &9 &wrap &prop
              ~ sys'dynamics'group_member ^append
              // :: loop method
              &3 &__run &std'patterns'ewhilenotzero &9 &wrap &prop
              ~ sys'dynamics'group_member ^append
              // 10: in action
              &__save &__get &nil 'program'input &nil &action &wrap
              // build command batch
              &cast
              $body
              &9
              ^ invoke;
inc        => &4 |= $body;
dec        => &5 |= $body;
next       => &6 |= $body;
prev       => &7 |= $body;
out        => &8 |= $body;
in         => &10 |= $body;
while      => &3 &__eval &cast $body &wrap &echo |=;