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 the message or 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 execute its content: first it puts the literal constant into the stack, then it calls system'console symbol and saves the result into the stack as well. 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.

Tuesday, May 17, 2016

ELENA 2.0.1 is out now

A new major release of ELENA programming language is available : 2.0.1

What's new:

  • ELENA
    • new statement #extern - exclude the code from GC
    • new simplified syntax to declare typified variable / field
    • new simplified syntax to declare subject
    • typecasting message is auto generated
    • #201 : shorthand property settings
    • #54 : templates
    • #231 - new syntax : Signature / Message constants
    • #128 : constructors may now return constant value
    • #136 : refactoring access to primitive managed code (core_routines)
    • #40 : self is typified for type extensions
  • ELC
    • binary incompatible due to implementing issues #217 and #54
    • #217 : output code optimization
    • critical bug fixes in COREX
    • critical bug fixes in GCX
    • #228 : safe region
  • LIB
    • system : BitArray, BitArray32
    • system'routines : PatternFilter2,PatternFilter3,PatternFilter4, PatternFilter5, PatternFilter6
    • system'routines : enumerable.filter&pattern[1]
    • system'routines : enumerable.group&by[1]
    • system'routines : enumerable.order&by[1]
    • system'routines : enumerable.get&count
    • system'io : stream.set&length[1]
    • system : Char#class.new[1] supports now an integer parameter
    • system'text : TextBuffer => TextBuilder
    • system'io : Console output operation can be redirected now
    • system'collections : #234 issue - listof:subject
  • IDE
    • issue #4 : IDE and Chinese characters
    • Menu - View - ELENA Interactive (#58: vm console)
  • Samples
    • rosetta sample : Top rank per group
    • rosetta sample : Truncatable primes
    • rosetta sample : Visualize a tree
  • ElenaScript
    • #65 issue : script engine is optimized

Wednesday, February 10, 2016

ELENA 1.9.25 : what's new

The latest release contains several critical bug fixes in the garbage collection algorithm. Currently GC for single thread application looks pretty stable. GC for multi-threading applications is still not 100% operational. It works quite ok for anagram sample (probably the most memory consuming STA program up to date). But chatserver (multi-threading program) crashes. GC is the prime suspect. Lock implementation was fixed as well.

Several critical bug fixes were done for structure fields (#216) and open arguments(#211).

Several samples were ported to Linux platform. It is still behind Windows one. When Linux debugger will be finished, probably the work will be accelerated.

A lot of small enhancements were done for LIB30. There are 100 Rosetta code task implemented for ELENA now. As a result new functionality was introduced in system'routines, extensions packages.

Tuesday, February 9, 2016

ELENA 1.9.25 is out (win32 / linux32)

ELENA 1.9.25 is out

A new release of ELENA programming language is available : 1.9.25

Linux Debian package includes now examples (\home\elena-lang) and LIB30 source code (\usr\local\src\elena).

For windows the path to elenart.dll should be included into PATHS. When the setup file is used it is done automatically.

What's new:

  • ELC
    • binary incompatible due to implementing issues #134 and #136
    • issue #132 : GC hotfix
    • issue #216 : operation with structure fields in stacksafe method
    • issue #211 : Message call sequence with open arguments
    • critical bug fix in lock statement
  • LIB
    • system'routines : enumerable.sequenceEqual[1], indexable.reverse&at[2]
    • system'routines : enumerable.accumulate&with[2]
    • extensions : textwriter.write&args[1,], textwriter.writeLine&args[1,]
    • extensions'text : (literal/wide).toByteArray
    • system : Message.get&literal$
    • system'io : MemoryBuffer
    • #213 : system'MethodNotFoundException provides the message name now
  • Samples
    • linux : anonymrec sample ported
    • linux : aplusb sample ported
    • linux : arraycallback sample ported
    • linux : arithmeticint sample ported
    • linux : arithmmean sample ported
    • linux : arrayconcat sample ported
    • linux : arraymode sample ported
    • linux : arrays sample ported
    • linux : associativearrays sample ported
    • linux : amb sample ported
    • linux : anagram sample ported
    • linux : arithmeval sample ported

Tuesday, August 11, 2015

ELENA 1.9.21 build (win32 / linux32) is out

A new release is available at github

What's new:

  • ELENA
    • new "variable" hint
    • "ByRef" parameters are supported
    • project template console_mt (corex) is operational once again
  • ELC
    • issue #47 fixed
    • issue #22 fixed
    • issue #68 fixed
    • issue #67 fixed
  • LIB
    • system'collections: Stack, Queue
    • system'dynamic: CastOver
    • bool inline if : iif[2]
    • forms : Combobox
    • system'dynamic'DynamicStruct is renamed into system'dynamic'Dynamic
  • Samples
    • rosetta code sample : 100 doors
    • rosetta code sample : 24 game
    • calc is using script engine now
  • IDE
    • issue #7 fixed
    • issue #74 fixed
 

Stack allocated variables in ELENA

In many programming languages numbers are immutable, so result of any arithmetic operation is a new number. For an object-oriented language it means creating a new object, putting an extra load to GC. Different languages choose different strategies to fight this problem.

In ELENA it is possible to declare embeddable objects which will be stack allocated. All basic numeric classes (IntNumber, LongNumber, RealNumber, ...) are embeddable.

Declaring a stack allocated object is quite straightforward - we have to declare a strong typed variable

   #var(type:int)i := 0.

The hint "type" tells the compiler that we creates a variable which is bound to system'IntNumber (int is a strong type associated with it). If the class is embeddable then it will allocate a space in the method stack and copies 0 into it.

The following operation

   #var(type:int)m := 2 * i + 1.

will be executed directly without allocating any additional space in the program heap.

But if we will pass it into the message call

   console writeLine:(a@n).

the compiler will box it (create an object copy in the heap). Moreover if the object is changeable after the operation the object will be unboxed (content of the dynamic object will be copied into original variable). In our case IntNumber is immutable, so no need to unbox.

In most cases we have to pass our number to one or another method. So there should be a way to reduce explicit boxing. It is possible to declare a method to be stack safe. As a result this will allow the compiler pass stack allocated variables directly. The only limitation that it should be a direct / virtual message call (it means that the target should be known. i.e. "typecasted" and be sealed or limited). Many classes provide such an interface, for example - an array

   console writeLine:
              (a array getAt &int:n).

In this case n is passed directly. Note that by sending get&array message to a, we typecast it.

This approach works quite good, except it cannot be used for returning values.

   #var(type:int)l := a array length.

In this case a new dynamic object is created despite using a stack allocated variable and known operation - get&length.

So we have to introduce a new concept - the type wrapper. If the class is sealed and has only one typed embeddable field it can be marked as a wrapper. In this case it could be used as by-reference parameter.

   #var(type:int)l := 0.
   a array readLength &vint:l.

vint is a strong type associated with Integer class. Integer is a wrapper around IntNumber value.

Some magic has to happen here. In normal case the compiler should send vint message to boxed l variable. Because IntNumber does not support get&vint message the operation will be broken. But in this case the compiler knowns that vint is a wrapper around int, so it will implicitly box it into an instance of Integer class. Moreover readLength&vint is direct and stack safe so the compiler will be able to pass a reference to our variable directly without extra boxing / unboxing operations.

As a result we are able to read the array length into the variable without allocating the number in the heap.

As you may see using this simple tricks we were able to write quite optimized code.