Skip to content

ELENA in a nutshell

Aleksey Rakov edited this page Nov 2, 2020 · 32 revisions

ELENA is a general-purpose language with late binding. It is multi-paradigm, combining features of functional and object-oriented programming. It supports both strong and weak types, run-time conversions, boxing and unboxing primitive types, direct usage of external libraries. Rich set of tools are provided to deal with message dispatching : multi-methods, message qualifying, generic message handlers. Multiple-inheritance can be simulated using mixins and type interfaces. Built-in script engine allows to incorporate custom defined scripts into your applications. Both stand-alone applications and Virtual machine clients are supported.

Here we will learn ELENA in details. Let's start!

Simple program

To create a simple program in ELENA we have to create a source file (with an extension .l) and write the following code:

public program()
{
    console.writeLine("Here my first program in ELENA!")
} 

We may compile it without creating a project file (where elc is a command-line compiler):

elc program1.l

The output is:

ELENA Command-line compiler 5.2.404 (C)2005-2020 by Alex Rakov

Project : program1, Platform: STA Win32 Console
Cleaning up...
Compiling...
Parsing unnamed
Compiling unnamed

Successfully compiled

Linking...
Successfully linked

It will create program1.exe file which we can execute:

program1

with the following result:

Here my first program in ELENA!

Beside the executable file a module program1.nl was created. Let's look what inside. We could use Bycode Viewer:

ecv program1.nl

The output will be:

ELENA command line ByteCode Viewer 5.2.81 (C)2011-2020 by Alexei Rakov
program1.nl module loaded

>

Now let's look at the generated classes:

>?
class program

>

As you can see the program entry was compiled as a class. Let's look inside:

>program
@parent system'Object
@flag elSealed
@flag elStateless
@flag elRole
@method @function program.#invoke

>

Our function was compiled as a singleton with a single anonymous method #invoke. Let's dive more deep:

>program.1
@method program.#invoke
    open       1h
    pusha
    pushr      0
    pushr      0
    callr      system'console
    storesi    0
    movr       const : "Here my first program in ELENA!"
    storesi    1
    peeksi     0
    movm       mssgconst : "writeLine<system'String>[2]"
    callrm     system'$private'Console mssgconst : "writeLine<system'String>[2]"
    peekfi     1
    close
    quit
@end

>

What do we see? ELENA byte-codes are quite simple. Let's review some of them

opcode Description
pusha puts the object accumulator on the top of the function stack
pushr r puts a constant on the top of the function stack
callr r invokes a symbol
storesi i saves an object accumulator in the function stack at specified relative position
movr r assigns a constant to the object accumulator
peeksi i loads an object from the function stack at specified relative position to the object accumulator
movm m assigns a message constant to the data accumulator
callrm r m invokes a method m in r class

Once again let's look at our code. Now it is quite clear. We put console symbol and a string literal constant into the function stack and directly invokes system'$private'Console.writeLine<system'String>[2] method.

System and Program Entries

Is our program executed at the executable start? Let's look into the linker code:

// create the image
ident_t entryName = project->resolveForward(SYSTEM_ENTRY);
_entryPoint = emptystr(entryName) ? LOADER_NOTLOADED : linker.resolve(entryName, mskSymbolRef, true);
if(_entryPoint == LOADER_NOTLOADED)
    throw JITUnresolvedException(ReferenceInfo(SYSTEM_ENTRY));

where SYSTEM_ENTRY is defined as:

constexpr auto SYSTEM_ENTRY               = "$system_entry";                // the system entry

So the answer is no. There is a special entry which executes initialization code and invokes our program. The system entry is a symbol. Can I redefine it? Yes of course. Let's look at our code once again: $system_entry is resolved as a forward. What is a forward? In ELENA the forward reference is a reference which is resolved in linking-time. It means that this information should be provided when we compile the project.

Where $system_entry is defined? To find it out we have to look into how the project settings are configured in ELENA. When we compile it once again we will see that the following info:

ELENA Command-line compiler 5.2.408 (C)2005-2020 by Alex Rakov

Project : program1, Platform: STA Win32 Console

STA Win32 Console is a project template which settings we are copying from. This template can be based on another one and so on. The available templates are listed in elc.cfg configuration file:

<templates>
   <template key="console">templates\win32_console.cfg</template>
   <template key="mta_console">templates\win32_consolex.cfg</template>
   <template key="gui">templates\win32_gui.cfg</template>
   <template key="mta_gui">templates\win32_guix.cfg</template>
   <template key="vm_console">templates\vm_win32_console.cfg</template>
   <template key="lib">templates\lib.cfg</template>
</templates> 

So let's look into the forward section of win32_console.cfg :

<forwards>
   <forward key="$program">system'startUp</forward>
   <forward key="$system_entry">system'core_routines'sta_start</forward>
   <forward key="program">$elena'@rootnamespace'program</forward>
   <forward key="program'arguments">extensions'program_arguments</forward>
   <forward key="program'output">system'console</forward>
   <forward key="newLine">system'newLine</forward>
   <forward key="onStart">system'onConsoleStart</forward>
</forwards>

Here we are : $system_entry* is resolved as system'core_routines'sta_start reference. Let's look what inside:

>ecv system'core_routines
ELENA command line ByteCode Viewer 5.2.81 (C)2011-2020 by Alexei Rakov
C:\Alex\ELENA\lib50\system.core_routines.nl module loaded

>#sta_start    
>@symbol sta_start
    pushs      0
    callextr   extern : PrepareEM
    freei      1h
    open       1h
    reserve    5h
    pushs      -2
    callextr   $native'coreapi'initProgramHeader
    pushr      $forwards'$program
    pushr      $native'coreapi'seh_handler
    pushr      $native'coreapi'default_handler
    alloci     1h
    loadenv    
    savesi     0
    movf       -1
    callextr   extern : InitializeSTA
    freei      4h
    restore    7h
    close      
    quit       
@end

>

The relevant part of the code is invoking InitializeSTA function. This function initializes system environments and calls another forward symbol - $forwards'$program. Let's look once again into win32_console.cfg. $forwards'$program is defined as system'startUp. It is already manageable code. If it is redefined we will have our own system entry. Let's do it! We will provide a default exception handler to gracefully end the program in case of an error and wait until the user press any key

// system entry
public mySystemEntry = startUp();

// system boot code
startUp()
{
   try
   {
      // invoking a program entry
      program()
   }
   catch(Expression ex)
   {
      console.writeLine(ex.toPrintable())
   };

   // wait for any key
   console.readChar()       
}

// a program entry
public program()
{
   console.writeLine("Here my first program in ELENA!")
} 

Now we could compile the program:

elc program2.l -f$program=program2'mySystemEntry

When we execute the program it will wait until a user presses any key.

Defining a forward

Let's look into the system entry code:

>ecv program2.nl
ELENA command line ByteCode Viewer 5.2.82 (C)2011-2020 by Alexei Rakov
program2.nl module loaded

>$private'startUp.1
@method $private'startUp.#invoke
    open       1h
    pusha      
    hook       Lab00
    movr       const : 'program
    movm       mssgconst : "#invoke"
    callrm     'program mssgconst : "#invoke"
    unhook     
    jump       Lab01
    Lab00:     nop
    popa       
    flag       
    and        200000h
    ifn        Lab02 0h
    load       
    peeksi     0
    callvi     0
    Lab02:     nop
    unhook     
    pushr      0
    pushr      0
    storesi    0
    movr       const : '$inline0
    storesi    1
    peeksi     0
    movm       mssgconst : "on[2]"
    callvi     0
    Lab01:     nop
    callr      system'console
    pusha      
    movm       mssgconst : "readChar[1]"
    callrm     system'$private'Console mssgconst : "readChar[1]"
    peekfi     1
    close      
    quit       
@end

The part, we are interested in, is the following:

    movr       const : 'program
    movm       mssgconst : "#invoke"
    callrm     'program mssgconst : "#invoke"

As you see the reference is resolved directly. This is not practical if we would like to reuse the system entry in different projects.

This is a typical use-case for a forward. First we have to rewrite the code slightly - by adding a forward attribute to the program reference :

// ...
startUp()
{
   try
   {
      // invoking a program entry
      forward program()
// ...

When we compile the code the result will be the same as before. Let's look once again in startUp:

@method $private'startUp.#invoke
    open       1h
    pusha      
    hook       Lab00
    callr      $forwards'program
    movm       mssgconst : "#invoke"
    callvi     0

As you see the reference was changed. It is no longer the direct one. But I do not define the forward. Why there was no error? The trick is in win32_console.cfg forward section. Look:

<forwards>
   <forward key="$program">system'startUp</forward>
   <forward key="$system_entry">system'core_routines'sta_start</forward>
   <forward key="program">$elena'@rootnamespace'program</forward>

First of all, program forward is already defined, it refers to $elena'@rootnamespace'program. Here comes the compiler magic - the namespace $elena'@rootnamespace is automatically replaced with the current project default namespace. In our case it is program2. Still we could easily redefine it. Let's implement alternative program:

public alt_program()
{
   console.writeLine("Here my second program in ELENA!")
} 

And compile the project as follow:

elc program2.l -f$program=program2'mySystemEntry -fprogram=program2'alt_program

After the successful compilation the program will generate the following output:

Here my second program in ELENA!

Here we are. As you see it is quite easy to declare and resolve forward references in any ELENA program!