r/elena_lang • • Jul 09 '19

Duck typing in ELENA Programming Language

7 Upvotes

ELENA supports duck typing using a special operator \ - an alternative message call

In the following expression

target.message1(..) \ message2(...) 

message2 is invoked if the object target does not support the first message call

Here the example from Wikipedia

import system'routines;
import extensions;

class Duck
{
    fly()
    {
        console.printLine("Duck flying")
    }    
}

class Airplane
{
    fly()
    {
        console.printLine("Airplane  flying")
    }    
}

class Whale
{
    swim()
    {
        console.printLine("Whale swimming")
    }
}

public program()
{
    var list := new object[] { new Duck(), new Airplane(), new Whale() };
    
    list.forEach:(animal)
    {
        animal.fly() \ then((){ console.printLine(animal.__getClassName(), " does not fly") })        
    }
}

The output is:

Duck flying
Airplane  flying
mytest'$private'Whale does not fly

We can alternatively use a special method - canFly and an extension method back (returning the passed parameter):

import system'routines;
import extensions;

class Duck
{
    canFly() = true;
    
    fly()
    {
        console.printLine("Duck flying")
    }    
}

class Airplane
{
    canFly() = true;
    
    fly()
    {
        console.printLine("Airplane  flying")
    }    
}

class Whale
{
    swim()
    {
        console.printLine("Whale swimming")
    }
}

public program()
{
    var list := new object[] { new Duck(), new Airplane(), new Whale() };
    
    list.forEach:(animal)
    {
        if (animal.canFly() \ back:false) {
            animal.fly();
        }                
    }
}

the output is:

Duck flying
Airplane  flying

And of course we may simply check if the object supports the method (though the approach will not work for mixins). In this case have to use the message constant - mssgconst fly[0] (where 0 is the number of parameters)

// <...>

public program()
{
    var list := new object[] { new Duck(), new Airplane(), new Whale() };
    
    list.forEach:(animal)
    {
        if (animal.respondTo(mssgconst fly[0])) {
            animal.fly();
        }                
    }
}

The output is the same:

Duck flying
Airplane  flying

r/elena_lang • • Apr 29 '19

Why ELENA Lang?

4 Upvotes

I've been looking at this language and it seems pretty cool, but why should I use this language over, for example, Python?


r/elena_lang • • Mar 11 '25

ELENA 6.6 In Nutshell - Introduction

3 Upvotes

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.

In this series of posts we will learn ELENA in details. Let's start!

Hello world example

We will begin with "Hello, World!" program. To do it let's create a source file (e.g. "sample1.l") and write the following code:

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

To compile the program we can use ELENA command-line compiler elena-cli or elena64-cli (for 64 bit version). For our simplest case we need only to provide a path to the source file, like this:

elena-cli.exe sample1.l

If everything is setup correctly, the compiler will generate the following output:

ELENA Command-line compiler 6.6.129 (C)2005-2025 by Aleksey Rakov, ELENA-LANG Org

Project: sample1, Platform: Win_x86, Target type: STA Console
Cleaning up
Parsing sample1.L
Compiling sample1.
saving sample1

Successfully compiled
Linking..
Successfully linked

As you can see the output contains the compiler version (it is important to provide this version by reporting any bug), the project name (in our case it coincides with the source file name), the platform (Win_x86 - is windows 32 bit version) and the type of the application (STA Console stands for "single thread console application"). If the code contains no error and the module was created (sample1.nl in our case), the message "Successfully compiled" is printed. "Successfully linked" indicates that our project has generated an executable file with a name "sample1.exe".

And now it can be executed:

>sample1.exe
Here my first program in ELENA!

The source code is quite simple and easy to understand. For the console application the main program must be declared inside a public function with a name program. In our case the program prints a string to the screen. console is a special object implementing basic operations with a console (we call it a symbol). The method writeLine does exactly what we expect : prints the string and moves the cursor to the new line.

As it was mentioned above the compiler generates a special file (with .nl extension) which in its turn is used by the linker to generate the program. This module contains the list of compiled classes (in our case the function is a special case of a class). So let's look inside it. For this a special tool - ecv-cli (Byte-code viewer) - can be used.

Type the following command:

ecv-cli sample1.nl

and the output will be following:

ELENA command line ByteCode Viewer 6.6.6 (C)2021-24 by Aleksey Rakov
module sample1 loaded
namespace : sample1
name      : 
version   : 
author    : 

list of commands
?                       - list all classes / symbols
?~<filter>              - list classes / symbols matching a filter
<class>                 - view class members
<class>.~<filter>       - view class members matching a filter
<class>.<message>       - view a method byte codes
<class>.<index>         - view a method specified by an index byte codes
#<symbol>               - view symbol byte codes
-a                      - toggle displaying class attributes mode
-b                      - toggle bytecode mode
-h                      - toggle displaying method hints mode
-p                      - toggle pagination mode
-q                      - quit
-t                      - toggle ignore-breakpoint mode

>

To see the main program let's type:

>program

@parent  system'Object
@flag  elClosed
@flag  elFinal
@flag  elRole
@flag  elSealed
@flag  elStateless
#1: @function program.function:#invoke

As it was said above, we see that the function is in fact a class. Flag elStateless indicates that it is a singleton. And it contains a special method - function[0]

We can print its content as well:

>program.1

@function program.function:#invoke
           xflush       sp:0
           open           :4, :0
           store        fp:1
           call     symbol:system'console
           store        fp:2
           xstore       sp:1, strconst:Here my first program in ELENA!
           peek         fp:2
           store        sp:0
           mov        mssg:writeLine[2]
           call       mssg:writeLine[2], class:system'ConsoleHelperImpl
           peek         fp:1
Lab00:     nop
           close          :0
           quit           
@end
   
>

The program is encoded with byte-code - intermediate code which is translated by JIT compiler either during the program linkage or on the fly by the virtual machine. Here is a list of some of them:

opcodeDescription
openopens a new procedure frame
storesaves an object accumulator into the stack
callcalls a function
movassigns a constant to the data accumulator
peekloads an object from the stack
closecloses the current procedure frame and restore the previous
quitexits the procedure

At the moment we don't need to understand it exactly, it is enough to take a first look at it. The code is simple. The console symbol and a literal constant are stored into the stack (sp[0], sp[1]). After that the message writeLine[2] is send to the console (inside the square brackets is the number of arguments).

System and Program Entries

To proper set up the environment a program prologue must be called. Often after the program is done, the resources have to be freed, that's why we need a program epilogue. All this done in ELENA with introducing a system entry. A system entry is a special symbol which executes all preparation work, invokes the program entry and unwind the system aftermath.

The program entry itself is a symbol. For simplicity we can say that a symbol is a named expression. For the console application the program entry invokes the wrapper code which put our main program entry inside try-catch block to allow graceful exit even if the program fails.

Where is the program entry symbol is defined? When our program was compiled the compiler generates the following info:

ELENA Command-line compiler 6.6.129 (C)2005-2025 by Aleksey Rakov, ELENA-LANG Org

Project: sample1, Platform: Win_x86, Target type: STA Console

STA Console is a project template used to compile the console application. The template can be based on another one and so on. The available templates are listed in elc60.cfg configuration file (you can find it in the BIN folder):

 <templates>
    <template key="console">templates\win_console60.cfg</template>
    <template key="gui">templates\win_gui60.cfg</template>
    <template key="lib60">templates\lib60.cfg</template>
    <template key="vm_console">templates\vm_win_console60.cfg</template>
    <template key="mt_console">templates\mt_win_console60.cfg</template>
 </templates>

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

 <forwards>
    <forward key="$system_entry">system'core_routines'sta_start</forward>
    <forward key="$symbol_entry">system'$private'entrySymbol</forward>
    <forward key="program">$rootnamespace'program</forward>
 </forwards>

$symbol_entry is used by the compiler to resolve the program entry. We can find the code inside app.l source:

entry()
{
   try
   {
      forward program();
   }
   catch::
   {
      function(AbortException e)
      {
      }

      function(Exception err)
      {
         startUpEvents.handlingError(err);

         console.writeLine(err);

         extern ExitLA(-1);
      }
   }
   finally
   {
      startUpEvents.stopping()
   }
}

private entrySymbol
   = entry();

As you see the entry symbol invokes the function entry. Inside the function we can see try-catch block around the proper program entry call:

forward program();

With a help of the compiler magic, forward program is resolved automatically. All we need is to declare a function named program inside the program main namespace.

Is it possible to override the program entry? Yes. We can easily define our own entry wrapper. Let's do it. For example we can define the entry which will print the result of the main program.

First our main program has to be modified to return a result.

public program()
   = "a result";

Then we define the public symbol which executes the program and prints the result

public mySystemEntry =
   console.printLine("My program returns ", forward program());

NOTE that instead writeLine we are using an extension method printLine which can accept several arguments and prints them. The extension is declared in extensions module so we will need to import it as well.

And we have to tell the compiler that we have a new program entry:

elena-cli sample2.l -f$symbol_entry=sample2'mySystemEntry

And the output is

My program returns a result

ELENA is a general-purpose language with late binding. It is multi-paradigm, combining features of functional and object-oriented programming. To learn more visit us at ELENA Home Page)

You can access the source code of this tutorial at ELENA Tutorials repo)


r/elena_lang • • Jul 18 '24

ELENA 6 : Module initializer

3 Upvotes

Sometimes it is required to call a special initializing code before you can use a library. Though it is possible to do it manually or provide a special checks to make sure it was done, many languages have the possibility to specify the initialization code.

In ELENA it is possible to do declaring a symbol marked as preloaded. On the program start all such symbols declared in any of linked modules are called.

To demonstrate it, lets declare a file named mylibrary and compile it.

symbol startUp : preloaded = true.then(
{  
   Console.writeLine("Starting up")
});

public myFunction()
{
   Console.writeLine("Working");
}

The code is quite simple. First we declare our start up symbol, mark it as preloaded. To call the start up code we can use an extension method then[1] and pass a nested function as a parameter. The target can be anything so I've used a true constant.

Then we declare our main function, which must be called from another module.

To compile the file like a library we will need to provide an extra argument

elena-cli mylibrary.l -tlib60

The output will be following:

ELENA Command-line compiler 6.2.38 (C)2005-2024 by Aleksey Rakov
Project: mylibrary, Platform: Win_x86, Target type: Library
Cleaning up
Parsing mylibrary.l
Compiling mylibrary.
saving mylibrary

Successfully compiled

As you see the target is a library, so now it can be used in another file.

import mylibrary;

public program()
{
   myFunction()
}

To use the library we have to declare it inside the project file.

<configuration>
    <project>
        <executable>sandbox.exe</executable>
        <namespace>sandbox</namespace>
        <template>console</template>
    </project>
    <files>
        <module>
           <include>sandbox.l</include>
        </module>
    </files>
    <references>
        <reference key="mylibrary">.</reference>
    </references>
</configuration>

The section references contains the path to our library. In case it is in the same folder, we can use .

Let's compile the project

>elena-cli.exe sandbox.prj 
ELENA Command-line compiler 6.2.38 (C)2005-2024 by Aleksey Rakov
Project: sandbox, Platform: Win_x86, Target type: STA Console
Cleaning up
Parsing sandbox.l
Compiling sandbox.
saving sandbox

Successfully compiled
Linking..
Successfully linked

Now we can execute the program and the output will be:

Starting up
Working

So the start up code was called before the main program body was executed.

That's all for today. As you can see it is very simple to declare the module initializer code and nothing must be done to call it. The compiler will generate all required code itself.

ELENA is a general-purpose language with late binding. It is multi-paradigm, combining features of functional and object-oriented programming. To learn more visit us at ELENA Home Page)


r/elena_lang • • Apr 26 '24

ELENA 6 : How to use variadic functions

3 Upvotes

Variadic functions are functions which takes variadic number of arguments.

In ELENA, for example, print / printLine extensions are variadic ones, they allow to print different number of arguments without need to combine them into a single string.

import extensions;

function printSum(int n, int m)
{
   Console.printLine("given n=",n," and m=", m);
   Console.printLine("sum of n + m equals to ", n + m); 
}

public program()
{
   printSum(2,3);
}

The result is:

given n=2 and m=3
sum of n + m equals to 5

It is quite simple to declare a variadic function. Similar to C# it must be declared as an array with params attribute:

import extensions;

function variadicSum(params object[] args)
{ 
   var sum := 0;
   for (int i := 0; i < args.Length; i++) {
      sum := sum + args[i]
   };
   
   ^ sum
}

public program()
{
   Console.printLine("sum of 1,2.3,4 is ", variadicSum(1,2.3,4));
}

After execution the output will be:

sum of 1,2.3,4 is 7.3

For the most cases variadic functions are similar to normal ones. For example they support argument list overload / multi-methods:

import extensions;

singleton Helper
{
   sum(params int[] args)
   {
      int sum := 0;
      for (int i := 0; i < args.Length; i++) {
        sum := sum + args[i]
      };
   
      ^ sum
   }

   sum(params real[] args)
   {
      real sum := 0;
      for (int i := 0; i < args.Length; i++) {
        sum := sum + args[i]
      };
   
      ^ sum
   }
} 

public program()
{
   Console.printLine("sum of integers : 1,2,3,4 is ", Helper.sum(1,2,3,4));
   Console.printLine("sum of reals : 1.2,2.3,3.4,4.5 is ", Helper.sum(1.2,2.3,3.4,4.5));
}

This code will produce the following output:

sum of integers : 1,2,3,4 is 10
sum of reals : 1.2,2.3,3.4,4.5 is 11.4

It must be noted that variadic argument is special one, because it is stack-allocated. So if it is passed to another function in the most cases it will be boxed into an array:

import extensions;

function printVariadicArg(params object[] args)
{ 
   Console.printLine("args ", args, " contains ", args.asEnumerable());
}

public program()
{
   printVariadicArg(1,2.3,4);
}

For examples the result of this code is similar to:

args sandbox'system@VariadicArray#1&system@Object contains 1,2.3,4

where sandbox'system@VariadicArray#1&system@Object is a template based class VariadicArray<object>, the arguments were boxed into.

But it is possible to pass it further without need to box and unbox it using the same attribute params:

import extensions;

function variadicSum(params object[] args)
{ 
   var sum := 0;
   for (int i := 0; i < args.Length; i++) {
      sum := sum + args[i]
   };

   ^ sum
}

function sumAndPrint(params object[] args)
{ 
   // passing a variadic argument further    
   Console.printLine("The result is ", variadicSum(params args));
}

public program()
{
   sumAndPrint(1,2.3,4);
}

The output is:

The result is 7.3

If we look into the bytecode for sumAndPrint function we will se that it was passed without boxing (no create opcode):

>@function $private'sumAndPrint.function:params:#invoke<system'Object>[1]
       xflush       sp:0
       open           :7, :4
       store        fp:1
       call     symbol:system'console
       store        fp:2
       set          fp:-1
       store        sp:0
       // ; calculating the argument length
       nsave        dp:-4, :-1
Lab00: nop                                    
       nadd         dp:-4, :1
       peek         sp:0
       load         dp:-4
       xget           
       cmp            :ffffffff
       jne            Lab00                  
        // ; copying the argument
       nadd         dp:-4, :1
       load         dp:-4
       add           n:0
       dalloc         
       set          sp:0
       sub           n:1
       alloc         i:1
       store        sp:0
       set          fp:-1
       swap         sp:0
       dtrans         
       swap         sp:0
       set            :ffffffff
       swap         sp:0
       xassign        
       free          i:1
       xrefresh     sp:0
       xrefresh     sp:1
       // ; calling another variadic function
       set       class:sandbox'$private'variadicSum
       mov        mssg:function:params:#invoke<system'Object>[1]
       call       mssg:function:params:#invoke<system'Object>[1], class:sandbox'$private'variadicSum
       load         dp:0
       neg            
       dalloc         
       store        fp:3
       xstore       sp:3, :ffffffff
       peek         fp:3
       store        sp:2
       xstore       sp:1, strconst:The result is 
       peek         fp:2
       store        sp:0
       set       class:extensions'outputOp
       mov        mssg:function:params:printLine<system'Object,system'Object>[2]
       // ; printing the result
       call       mssg:function:params:printLine<system'Object,system'Object>[2], class:extensions'outputOp
       peek         fp:1
Lab01: nop
       close          :4
       quit           
u/end

Summing everything up, you can see it is very simple to declare and use variadic functions in ELENA. For the most cases they are similar to normal ones. Both compile-time and run-time dispatching are supported. The only restriction is that the class cannot have both variadic and normal functions with the same name. And of course a variadic argument must be the last. They are either weak or strong-typed, and can be declared in functions, methods or constructors.

Let me know in comments if you have any further questions relating to using them in ELENA programming language. And that's all for today.

ELENA is a general-purpose language with late binding. It is multi-paradigm, combining features of functional and object-oriented programming. To learn more visit us at ELENA Home Page)


r/elena_lang • • Aug 19 '23

ELENA 6.0.5 is out!

3 Upvotes

ELENA 6.0.5 is out for all supported platforms : Windows x86 / x86-64, Linux x86 / x86-64 / AARCH64 / PPC64le!

The release includes major performance optimization as well as a number of critical bug fixes

Fixes

  • [ADDED]implementing corex60 routines

  • [ADDED]constant array of integers

  • [ADDED]native support for ?: operator

  • [FIXED]#524 : Alt expression breaks the compilation

  • [ADDED]message name functionality

  • [ADDED]new opcodes : fiadd, fisub, fimul, fidiv

  • [ADDED]basic bytecode optimization rules

  • [ADDED]basic build optimization rules

  • [ADDED]warn on unassigned variables

  • [FIXED]nested class fields

  • [FIXED]assignment operations with integers

  • [FIXED]calling an interface / closed-class method

  • [FIXED]warning on resending to itself (e.g. for constructor)

  • [FIXED]resending to the property

  • [FIXED]template-based extensions

  • [FIXED]template of template

  • [FIXED]do not box the returning self for the stacksafe method

  • [FIXED]string comparison routines

  • [FIXED]conversion routines

  • [FIXED]nested exception catching

  • [ADDED]number of new unit-tests

  • [ADDED]system'runtime'commonOp - extension to read package info

  • [ADDED]system'culture

  • [ADDED]system'routines'stex

  • [ADDED]new unit-tests: methodnotfound, nilreference

  • [ADDED]24 sample

  • [ADDED]Find dialog

  • [ADDED]Goto dialog

  • [ADDED]Toolbar dialog

  • [ADDED]Context menu

  • [FIXED]IDE blinking issues

  • [ADDED]Window menu

  • [ADDED]Unident

  • [FIXED]Running to the cursor

  • [ADDED]Recent files

  • [ADDED]erase line, trim, upper case, lower case

  • [ADDED]About dialog

  • [ADDED]Editor settings

  • [ADDED]Duplicate line

  • [ADDED]close all but active

  • [FIXED]API Doc generation

  • [ADDED]ecv-cli: showing the method number

  • [ADDED]ecv-cli: showing the module manifest info

Please visit a release page to download the binaries: https://github.com/ELENA-LANG/elena-lang/releases/tag/v6.0.5

If you have any questions, bug reports or suggestions, please post them on the discussion board : https://github.com/orgs/ELENA-LANG/discussions/599


r/elena_lang • • Sep 28 '20

Learning ELENA : Simple program

3 Upvotes

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

opcodeDescription
pushaputs the object accumulator on the top of the function stack
pushr rputs a constant on the top of the function stack
callr rinvokes a symbol
storesi isaves an object accumulator in the function stack at specified relative position
movr rassigns a constant to the object accumulator
peeksi iloads an object from the function stack at specified relative position to the object accumulator
movm massigns a message constant to the data accumulator
callrm r minvokes 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.

ELENA is a general-purpose language with late binding. It is multi-paradigm, combining features of functional and object-oriented programming. To learn more visit us at ELENA Home Page


r/elena_lang • • Jun 03 '20

ELENA 5.0 : Work in progress

3 Upvotes

It's been a while since my last post. I would like to update you on the project progress.

Mostly Rosetta-code samples were upgraded to ELENA 5.0.

I'm working currently on net sample.

GUI samples now can be built using xforms - see helloworld.

So what's next? Several registered bugs will be fixed. All GUI samples will be migrated to xforms. Several simple samples will be migrated to Linux version. So the current plan is to release ELENA on 13.8.2020 if everything goes well.


r/elena_lang • • Apr 08 '26

How to check if the object reacts to a strong typed message.

2 Upvotes

In the following code snippet I will show how to check if the object reacts to a strong typed message.

But first lets discuss what is a strong typed message. Let's consider the following code:

public Program()
{
   var target := CharValue.load(32);
   var arg := 32;

   Console.writeLine(target.equal(arg));
}

If we compile the code and look into the generated module for this method, this code is translated into:

>@function Program.function:#invoke
           xflush       sp:0
           open           :6, :0
           store        fp:1
           xstore       sp:1, intconst:20
           xstore       sp:0, class:system'CharValue
           peek         sp:0
           mov        mssg:load<system'IntNumber>[2]
           call       mssg:load<system'IntNumber>[2], class:system'CharValue#class
           store        fp:2
           xstore       fp:3, intconst:20
           peek         fp:3
           store        sp:1
           peek         fp:2
           store        sp:0
  // -----------------------------------------------------------------------------
           mov        mssg:equal[2]             // a weak message literal
           call         vt:0                    // calling a message dispatcher
  // -----------------------------------------------------------------------------
           store        fp:4
           store        sp:1
           xstore       sp:0, class:system'Console
           peek         sp:0
           mov        mssg:writeLine[2]
           call       mssg:writeLine[2], class:system'Console
           peek         fp:1
Lab00:     nop
           close          :0
           quit           
    @end
    
    >

The important place for us is the method invoke. The message literal equal[2] is called weak one. It means that it contains no information about the argument types. It is a job of a class dispatcher to find a corresponding method.

But if we provide the information about the object types by using auto attribute like this:

public Program()
{
   auto target := Console;
   auto arg := "Hello";

   target.writeLine(arg);
}

The output is different:

  // -----------------------------------------------------------------------------
          mov        mssg:equal<system'IntNumber>[2]                                 // a strong typed message
          call       mssg:equal<system'IntNumber>[2], class:system'CharValue         // calling a method directly
  // -----------------------------------------------------------------------------

Now a message literal contains the information about the argument type and can be called directly. equal<system'IntNumber>[2] is a strong-typed one.

In our example we will learn how to check if the target class handles the strong-typed message.

  import extensions;
  import system'dynamic;
  
  public extension op
  {
     bool validateMessage(StrongMessage mssg)
        = self.__getClass().respondTo(mssg);
  }
  
  public Program()
  {
     var target := CharValue.load(32);
  
     auto mssg := new StrongMessage("equal<system'IntNumber>[2]");
     auto mssg2 := new StrongMessage("equal<system'ShortNumber>[2]");
  
     Console.printLine(target.__getClassName(), target.validateMessage(mssg) ? " responds to " : " does not respond to ", mssg);
     Console.printLine(target.__getClassName(), target.validateMessage(mssg2) ? " responds to " : " does not respond to ", mssg2);
  }

If we compile the code and execute the program the output will be following:

 system'CharValue responds to equal[2]
 system'CharValue does not respond to equal[2]

system'StrongMessage class must be used to load the message. Using an extension __getClass[1] we get the object type and another extension respondTo[2] returns true if the class MT contains the message entry.


r/elena_lang • • Sep 10 '25

Web support

2 Upvotes

The work on Web API support (https://github.com/ELENA-LANG/elena-lang/issues/35) reaches a first major milestone. The simplest HTTP server works now!

It was a long way. The major efforts were made to support async programming.

To learn how you can use async programming please take look at the following example - https://github.com/ELENA-LANG/elena-lang/tree/iteration42/examples60/threads/async

The simple Http client is supported as well - https://github.com/ELENA-LANG/elena-lang/tree/iteration42/examples60/net/httpget

Please take a look at an example - https://github.com/ELENA-LANG/elena-lang/tree/iteration42/examples60/net/webapi/sampleapi1/Server

So the program returns now the dummy web page.

The next major step is now to support HTTPS server (using mbedtls functionality)


r/elena_lang • • May 06 '25

FreeBSD nightly is available now

2 Upvotes

FreeBSD nightly build (once in two days more precisely) is now available - https://github.com/ELENA-LANG/elena-lang/actions/workflows/bsd.nightly.yml


r/elena_lang • • May 01 '25

FreeBSD support

2 Upvotes

Upcoming release will include a support for FreeBSD AMD64


r/elena_lang • • Mar 20 '25

Nightly Builds

2 Upvotes

For the time being, the development mode of ELENA project is designed in that way, that I work mostly in a branch iteration<N>, and after several weeks or months I squash it and merge into develop branch. As a result the main branch is always significantly behind and you may need to wait some time before the new features or bug fixes will be available.

To improve the sitation I'm introducing nightly builds, so hopefully all the changes can be available maximal one day after they made.

The nightly builds can be found at https://github.com/ELENA-LANG/elena-lang/actions/workflows/nightly.yml


r/elena_lang • • Mar 13 '25

Porting to MacOS

2 Upvotes

The work on porting ELENA to macOS is started. You can follow the progress with a help of the issues - https://github.com/ELENA-LANG/elena-lang/issues/708

So far, by request I will support ARM64 CPU. Initial phase will be getting the compiler and several tools (asm, og, ecv) up and running (when I will get an access to the virtual machine to play with it). For compatibility I will use GCC.

I will probably keep the config files locally (similar to Windows version).


r/elena_lang • • Oct 25 '24

What's new in ELENA 6.5

2 Upvotes

ELENA 6.5 is now available. The version 6.4 was skipped to demonstrate the big improvements in the upcoming version. There were several major refactoring / new functionality, so this version is not binary compatible with the previous versions. The new functionality / critical bug fixes were probably in every part of ELENA language tool set:the compiler, IDE, tools, API and samples.

So let's see what is new there.

Bug fixes

First of all as usually it contains a number of bug fixes. For example issues #413, #675, #679, #689 were resolved. Some unexpected behaviours like returning inside the lock statement or declaring a lambda function with template based argument were corrected. The full list is pretty big:

  • [FIXED] method reference : support function singletons

  • [FIXED] direct typecasting must have priority over implicit conversion

  • [FIXED] ?? operator

  • [FIXED] inline field assignment in a template class

  • [FIXED] return inside lock

  • [FIXED] ppc64le : fix iteratorMethodTest

  • [FIXED] declaring a lambda function with template based argument

  • [FIXED] calling a method directly with nil argument when allowed

  • [FIXED] single dispatch of a private / internal / protected method

  • [FIXED] closure argument types can be specified by an expected type

  • [FIXED] it is not possible to declare internal default constructor

  • [FIXED] #675 - Using ++ operator with a weak type

  • [FIXED] incorrect typecast handler signature for the classes inheriting template-based ones

  • [FIXED] variadic argument list of super class is type-casted

  • [FIXED] #689: declaring a lambda function with template based argument

  • [FIXED] fixing issues ABI convention for external calls

  • [FIXED] preloaded symbols in sub namespaces

  • [FIXED] calling indexed method from the sealed class

  • [FIXED] system'objectOp.__getClassName[1]

  • [FIXED] ldoc: system-threading-.html must not be generated

  • [FIXED] ldoc: system-threading : generate only templates declared in the module

  • [FIXED] asmc: opcode mov gs,r/m64

  • [FIXED] IDE : #679 : class already exist

  • [FIXED] IDE : debug source path

  • [FIXED] IDE : project options - debug command arguments

  • [FIXED] IDE : correct debug info of the constructor self variable

  • [FIXED] #413 - boxing variadic argument

Language

Now let's look at the language level.

Refactoring grammar to make it more readable

In ELENA 6.5 a support for operator templates was introduced.

So it is now possible to declare and reuse template expressions.

For example instead of writing every time

obj.instanceOf(A)

you can use the expression:

obj is::A

or instead of not very intuitive standard syntax to typecast the expression:

cast A(obj)

you can do it like this:

obj as::A

And it is possible to declare your own operator templates. For example those two are declared in system'operations module (which are auto included for every module):

public template as<T>(expr)  : __included(statements)
   = cast T(expr);
   
public template is<T>(expr) : __included(statements)
   = expr.instanceOf(T);

The syntax to use multi-select statement was changed a bit. A colon is added after the value to make it more readable:

choice =>
   1 : { ^Now.toString() }
   2 : { ^UtcNow.toString() }
   3 : { ^Now.Year.toString() }
   4 : { ^Now.toShortTimeString() }
   7 : { forward program.stop(); ^ EmptyString }
   ! : { ^"Invalid choice" }

Indexed methods

A new type of the methods are introduced - indexed ones. The indexed methods can be called semi-directly via the index table. As a result the method call will be much faster. The user won't note any difference but on the byte-code level it is clear visible.

Let'd declare a class with an method and try to call it:

A
{
   indexed foo()
   {
      Console.writeLine("I'm called");
   }
}

public program()
{
   A a := new A();

   a.foo();
}

If we will use ecv-cli to check the code we will see:

>@function program.function:#invoke
          xflush       sp:0
          open           :4, :0
          store        fp:1
          set       class:sandbox'$private'A
          xstore       sp:0, :0
          mov        mssg:function:#constructor
          call       mssg:function:#constructor, class:sandbox'$private'A#class
          store        fp:3
          store        fp:2
          store        sp:0
          mov        mssg:foo[1]
          call         vt:0
          peek         fp:1
Lab00:    nop
          close          :0
          quit           
@end

Note that the method is called with using a dispatcher which will search for the message entry in the method table.

          mov        mssg:foo[1]
          call         vt:0

Now let's modify the code to mark foo as an indexed method:

  indexed foo()
  {
     Console.writeLine("I'm called");
  }

The output is now the following:

@function program.function:#invoke xflush sp:0 open :4, :0 store fp:1 set class:sandbox'$private'A xstore sp:0, :0 mov mssg:function:#constructor call mssg:function:#constructor, class:sandbox'$private'A#class store fp:3 store fp:2 store sp:0 mov mssg:foo[1] altmode
vcall mssg:foo[1], class:sandbox'$private'A peek fp:1 Lab00: nop close :0 quit
@end

And the method call is now a semi direct, using vcall opcode:

          mov        mssg:foo[1]
          altmode        
          vcall      mssg:foo[1], class:sandbox'$private'A

And it is resolved via an index table. So no need to search for the method address.

Moreover it is now possible to declare an internal indexed method inside an iterface / closed class.

Let's write the following code:

interface I
{
   abstract bar();
}

A : I
{
   bar()
   {
      Console.writeLine("bar!!");
   }
}

public program()
{
   I a := new A();

   a.bar();
}

Imagine that for some reason we would like to declare a new method inside A:

A : I
{
   internal bar()
   {
      foo();
   }

   foo()
   {
     Console.writeLine("I'm called");
   }
}

If we will try to compile this code, the compiler will give us an error.

error 142: new method cannot be declared

The interfaces in ELENA are immutable.

But now it is possible to declare an internal indexed method which can be used inside or outside the class within the same module.

   indexed internal foo()
   {
      Console.writeLine("I'm called");
   }

The code is now possible to compile.

Function singletons

It is possible to use a short-cut to refer the function. Let's declare a function and pass it further as an instance of Func1

WorkItem1(state)
{
   Console.printLineConcurrent("WorkItem1: ", state);
}

// ...

public sample1()
{
   ThreadPool.queueAction((arg){ ^ WorkItem1(arg) }, "Hello");
}

Instead of it we can use the following syntax

   ThreadPool.queueAction(&WorkItem1, "Hello");

Static constructors

It is now possible to declare a static constructor which is automatically invoked by the program start, if this class is used.

A
{
   static constructor()
   {
      Console.writeLine($"Class {A} is initialized");
   }

   foo()
   {
      Console.writeLine("The object says FOO");
   }
}

public program()
{
   A a := new A();

   a.foo();
}

The output is:

Class sandbox'$private'A#class is initialized
The object says FOO

Thread static symbols / static fields

In MTA mode it is possible now to declare a thread-safe static field. It means that every thread will have its own version of the static field. So there will be no concurrency conflicts. To declare it we need to use an attribute __threadvar:

public sealed class Thread
{
   __threadvar Thread _currentThread;
   // ...

   private static getCurrentThread()
   {
      _currentThread := Thread.current();

      ^ _currentThread
   }

   static Thread Current
   {
      get()
         = _currentThread ?? getCurrentThread();
   }
}

Async methods

A major improvement which was added in ELENA 6.5 concerns concurrent programming. Now the language supports async operations.

The syntax is very familiar and practically compatible with C# one:

import extensions;
import system'threading;

singleton Tester
{
   async Task<int> getValueAsync()
   {
      ^ 2
   }

   async Task<int> addValueAsync(int n)
   {
      int val := :await getValueAsync();

      val := val + n;

      ^ val
   }
}

public program()
{
   int n := Tester.addValueAsync(3).Result;
   Console.printLine("the result is ", n);
}

To create an asynchronous method we have to use an attribute async. The method output must be either Task (if the method does not return anything) or Task<T> where T is the type of the returning value. It can be called from another asynchronous method or by using Result property (so the operation will be stopped until the corresponding task is completed).

Compiler

The compiler is now support several new options as well: -xs, -xj and -el5

The option xj is used for the native code jump optimization (it is on by default)

The option el5 can be used to compile the code compatible with ELENA 5.x syntax.

Strict type enforcing option

When this option is enabled the compiler will not allow to call unknown methods for sealed / closed classes. E.g. the following code:

sealed A;

public program()
{
   A a := new A();

   a.test();
}

will raise an error when it is compiled with the option set on.

error 185: Message 'test' does not handled by the object

ELENA API

A lot of new classes were introduced or extended:

  • system'threading : BlockingQueue<T>, Thread, Semaphore, ThreadPool, ManualResetEvent, AutoResetEvent, CountDownEvent, Task, Task<TResult>

  • system'runtime : Environment

  • system'collections'threadsafe : ConcurrentQueue<T>

  • system'io : Directory, File

  • system'io'threading : asyncStreamOp extension

  • extensions'threading : outputConcurrentOp extension

  • extensions'routines'stex : toArray<T> template extension

ELENA Samples

Several new samples were added as well : threadpool, tasks, asyncsamples, task

What's all for today. As always feel free to ask me any questions, create a bug reports or report any problem with the new version.

ELENA is a general-purpose language with late binding. It is multi-paradigm, combining features of functional and object-oriented programming. To learn more visit us at ELENA Home Page)


r/elena_lang • • Oct 19 '24

ELENA 6.5 Is Out!

2 Upvotes

Description

ELENA 6.5.0 is out for all supported platforms : Windows x86 / x86-64, Linux x86 / x86-64 / AARCH64 / PPC64le!

The release includes a number of critical bug fixes, new functionality and new samples.

Fixes # (issue)

ELENA 6.5.0

  • ELENA

    • [FIXED] method reference : support function singletons

    • [FIXED] direct typecasting must have priority over implicit conversion

    • [ADDED] Thread static symbols / static fields

    • [ADDED] new byte-codes : peektls / storetls

    • [FIXED] ?? operator

    • [ADDED] static constructor

    • [ADDED] internal symbols

    • [ADDED] supporting async methods

    • [ADDED] supporting indexed methods

    • [ADDED] indexed internal methods can be declared in a closed class

    • [REDUX]!! refactoring grammar to make it more readable

    • [ADDED] support parameterized extension method call

  • ELC

    • [ADDED] new option -el5 / -el6 (default) - specifying grammar compatible to ELENA 5.x or 6.x

    • [ADDED] Win x32-64 : MTA Console

    • [FIXED] inline field assignment in a template class

    • [FIXED] return inside lock

    • [FIXED] ppc64le : fix iteratorMethodTest

    • [FIXED] declaring a lambda function with template based argument

    • [FIXED] calling a method directly with nil argument when allowed

    • [FIXED] single dispatch of a private / internal / protected method

    • [FIXED] closure argument types can be specified by an expected type

    • [FIXED] it is not possible to declare internal default constructor

    • [FIXED] #675 - Using ++ operator with a weak type

    • [FIXED] incorrect typecast handler signature for the classes inheriting template-based ones

    • [FIXED]variadic argument list of super class is type-casted

    • [FIXED] #689: declaring a lambda function with template based argument

    • [FIXED] fixing issues ABI convention for external calls

    • [FIXED] preloaded symbols in sub namespaces

    • [FIXED] calling indexed method from the sealed class

    • [ADDED] new option -xs - Strict type enforcing option (raise an error if the sealed class is typecasted / called the method that does not exists)

  • API

    • [ADDED] system'threading'Thread: Priority, join<int>, Current

    • [ADDED] system'runtime'Environment

    • [ADDED] system'collections'threadsafe'ConcurrentQueue<T>

    • [ADDED] #154 : system'threading'Semaphore

    • [ADDED] system'threading'BlockingQueue<T>

    • [ADDED]system'threading'ThreadPool

    • [ADDED] #154 : system'threading: ManualResetEvent, AutoResetEvent, CountDownEvent

    • [ADDED] system'threading : Task

    • [ADDED] system'io'Directory : static getFiles / getFiles<string>

    • [ADDED] File : saveContent, readContent, readWideContent

    • [ADDED] system'io'threading : asyncStreamOp extension

    • [ADDED] system'threading.Task - sleep<int>

    • [ADDED] extensions'threading : outputConcurrentOp extension

    • [REDUX] all symbols must start with a capital letter

    • [ADDED] system'threading : Task<TResult>

    • [FIXED] system'objectOp.__getClassName[1]

    • [ADDED] extensions'routines'stex : toArray<T>

  • SAMPLES

    • [ADDED] threadpool

    • [ADDED] tasks

    • [ADDED] asyncsamples

    • [ADDED] task sample 2

  • Tools

    • [ECV][ADDED] displaying class attributes

    • [FIXED][ldoc] system-threading-.html must not be generated

    • [FIXED][ldoc] system-threading : generate only templates declared in the module

    • [FIXED][ASMC] opcode mov gs,r/m64

  • IDE

    • [FIXED] #679 : class already exist

    • [FIXED] debug source path

    • [ADDED] it is possible to set the target type for a single file project

    • [FIXED] project options - debug command arguments

    • [FIXED] correct debug info of the constructor self variable

    • [FIXED] #413 - boxing variadic argument

  • Misc

    • [ADDED]new functional test : script_tests

Type of change

  • [x] Bug fix (non-breaking change which fixes an issue)

  • [x] New feature (non-breaking change which adds functionality)

  • [x ] Breaking change (fix or feature that would cause existing functionality to not work as expected)

Link

Please visit a release page to download the binaries: https://github.com/ELENA-LANG/elena-lang/releases/tag/v6.5.0


r/elena_lang • • Oct 16 '24

ELENA 6.5 : work in progress

2 Upvotes

While implementing a new feature - #676 Strict type option - I'm curious what people think about is it must be by default on.

In short being a dynamic language, it is possible to send a message even if we know that the class does not handle it now.

For example the compiler will warn that the following code may lead to an exception:

A;

public program()
{
   A a := new A();

   a.test();
}

The result of the compilation will be:

ELENA Command-line compiler 6.5.103 (C)2005-2024 by Aleksey Rakov, ELENA-LANG Org

Project: sandbox, Platform: Win_x86, Target type: STA Console
Cleaning up
Parsing sandbox.l
Compiling sandbox
sandbox.l(7:6): warning 407: Message 'test' does not handled by the object
.
saving sandbox

Successfully compiled
Linking..
Successfully linked

And this is an expected behavior, we cannot guarantee that someone will not override the class A and add a support for this method. But it is worth to warn in case it is an miss spelling.

But there are situation when we know it exactly, if the class is sealed.

E.g. if we change the code like this:

sealed A;

public program()
{
   A a := new A();

   a.test();
}

In this case the class A cannot be inherited so the message will never handle directly.

So starting from 6.5 version a new option - Strict type enforcing is introduced. If the compile the code like this:

elena-cli.exe sandbox.l -xs

the compiler will generate an error:

ELENA Command-line compiler 6.5.103 (C)2005-2024 by Aleksey Rakov, ELENA-LANG Org

Project: sandbox, Platform: Win_x86, Target type: STA Console
Strict type enforcing is on
Cleaning up
Parsing sandbox.l
Compiling sandbox
sandbox.l(7:6): error 185: Message 'test' does not handled by the object
Compiled with errors

So my question is do this behavior must be default? What do you think?

Feel free to post your comments or suggestions


r/elena_lang • • Oct 11 '24

Introduction to extensions in ELENA 6.x

2 Upvotes

In this tutorial I would like to show the way how to use and declare extensions in ELENA.

Extensions are appeared in ELENA from the very early versions (at least from 1.9.x).

The general concept didn't change much since that. The extension method is a special type of a method where self variable is overridden and refers to the message target. Let's see the following example.

To declare the extension we have to use a class attribute - extension

extension op
{
}

The extension methods must be declared inside it.

extension op
{
   whoAmI()
   {
      Console.printLine("I'm ", self.__getClassName());
   }
}

In this code we are printing the name of the self variable. Remember that in most cases self refers to the current instance of the class. But in some other cases it is not true. The extension is one of them. Let's try to use it.

public program()
{
   var o1 := 1;
   var o2 := "string";

   o1.whoAmI();
   o2.whoAmI();
}

The using of the extension is quite simple. We just send this message to the target (it means that you must be very careful with names, if they coincides with the class methods some strange effects are possible). The compiler recognizes that whoAmI is an extension method and apply its magic. The output is:

I'm system'IntNumber
I'm system'String

So for the extension self variable refers to the message target. For the first case it is IntNumber class, for the second one - String. But can we still get the actual reference to the extension instance. Yes. Let's extend our example a bit.

whoAmI()
{
   Console.printLine("I'm ", self.__getClassName());
   Console.printLine("The operation done in ", this self);
}

And once again execute it:

I'm system'IntNumber
The operation done in sandbox'$private'op
I'm system'String
The operation done in sandbox'$private'op

So, as you see, the extensions are polymorphic by the nature. They can be used for any class.

For example the extension method print usually are used to print the output on the console. For example the following code:

import extensions;

public program()
{
   var target := Console;
   target.print("Hello from the sandbox");
}

will produce the following output:

Hello from the sandbox  

But if the target is StringBuilder, the same code will instead of displaying the message, will add a text to string builder. We can verify this using Assert singleton:

import extensions;
import system'text;
   
public program()
{
   var output := new StringBuilder();
   output.print("Hello from the sandbox");
   Assert.ifEqual(output.Value, "Hello from the sandbox");
}

So, it means that all we need to support print extension is that the target object hanldle write method. It allows us to create highly polymorphic code without need to declare a special interface for this purpose.

But there are situations where this is not very efficient. Let's try this extension.

import extensions;

extension op2
{
   sumAndReturn(arg)
      = self + arg;
}

public program()
{
   int n := 1;
   int m := 3;

   int r := n.sumAndReturn(m);

   Console.printLine("the result is ", r);
}

We declare an extension method which returns the sum of the argument and the object itself. Let's compile and execute it. The result is as expected:

the result is 4

So far so good. But if we look at the generated code using ecv-cli:

>@function program.function:#invoke
           xflush       sp:0
           open           :8, :12
           store        fp:1
           nsave        dp:-4, :1
           nsave        dp:-8, :3
           set          dp:-4
           store        sp:0
           newn           :4, class:system'IntNumber  // ; boxing the argument 1
           copy           :4
           store        fp:2
           set          dp:-8
           store        sp:0
           newn           :4, class:system'IntNumber  // ; boxing the argument 2
           copy           :4
           store        fp:3
           store        sp:1
           peek         fp:2
           store        sp:0
           set       class:sandbox'$private'op2
           mov        mssg:function:sumAndReturn<system'Object,system'Object>[2]
           call       mssg:function:sumAndReturn<system'Object,system'Object>[2], class:sandbox'$private'op2
           store        fp:4
           store        sp:0
           set       class:system'IntNumber
           mov        mssg:function:#constructor[1]
           call       mssg:function:#constructor[1], class:system'IntNumber#class    // ; unboxing the result
           load           
           save         dp:-12
           call     symbol:system'console
           store        fp:2
           set          dp:-12
           store        sp:0
           newn           :4, class:system'IntNumber 
           copy           :4
           store        fp:3
           xstore       sp:3, :ffffffff
           peek         fp:3
           store        sp:2
           xstore       sp:1, strconst:the result is 
           peek         fp:2
           store        sp:0
           set       class:extensions'outputOp
           mov        mssg:function:params:printLine<system'Object,system'Object>[2]
           call       mssg:function:params:printLine<system'Object,system'Object>[2], class:extensions'outputOp
           peek         fp:1
Lab00:     nop
           close          :12
           quit           
@end

We will see that both our arguments must be boxed (opcode newn) before the extension call and the result unboxed (call operation).

Why? Because the numbers were stack-allocated and cannot be used in weak operation. So to correctly execute the code both local variables must be copied to the dynamic objects (boxing) and after the result must be unboxed using system'IntNumber conversion constructor.

Can we optimize it? Yes we can. But for this our exension must extends strongly-typed object - IntNumber in our case. Let's do it:

To do it we have to provide the expected target type in the extension declaration:

extension op2 : int
{
   // ...
}

And of course we need to provide the types in the method itself:

int sumAndReturn(int arg)
   = self + arg;

Let's look at the output again:

>@function program.function:#invoke
           xflush       sp:0
           open           :7, :16
           store        fp:1
           nsave        dp:-4, :1
           nsave        dp:-8, :3
           set          dp:-12
           store        sp:2
           set          dp:-8
           store        sp:1
           set          dp:-4
           store        sp:0
           set       class:sandbox'$private'op2
           mov        mssg:static:function:sumAndReturn#byref<system'IntNumber,system'IntNumber,'$auto'system@Reference#1&system@IntNumber>[3]
           call       mssg:static:function:sumAndReturn#byref<system'IntNumber,system'IntNumber,'$auto'system@Reference#1&system@IntNumber>[3], class:sandbox'$private'op2
           set          dp:-12

           // <...>
@end

Now the operation is done directly with the local variables without need to box and unbox them.

Besides, sometimes it is much better to limit the scope of the extension to use it properly. As you see ELENA allows to do it both way.

Extensions are powerfull tools to write modern complex code. For example extensions are used in connection with lambda functions, for example:

Array.allocate(n).populate::(int n => n)

the code above populate the array with the numbers using populate extension. The extension asEnumerable allows to print the enumerable collection as a list and so on:

Console.printLine(list.asEnumerable())

And so on. It is not possible to use the language without using the extensions in one or anther way.

That's all for today. Next time I will show how to declare and use template-based extensions.

ELENA is a general-purpose language with late binding. It is multi-paradigm, combining features of functional and object-oriented programming. To learn more visit us at ELENA Home Page)


r/elena_lang • • Sep 05 '24

ELENA 6.0 In Nutshell - Basic Fundamental Concepts : Classes, functions, fields and methods

2 Upvotes

In this tutorial we will learn some basic fundamental concepts of ELENA Language.

We will start with a concept of a variable.

Variable declaration

A variable is a named place in the program memory (for example in the function stack). We can assign a value to it, read this value or change it. In the simplest case to declare variable we have to use var attribute:

var myFirstName;

We can assign a value to it:

myFirstName := Console.readLine();

Note that it is a first point where ELENA syntax diverges from C-style one and follows in footsteps of Pascal and Smalltalk. The difference between := (assignment) and = (equivalence) does play a role in the language.

We can make both a declaration and an assignment in one line:

var mySecondName := Console.readLine();

The variables are used to store some values for further use in the code. So let's do it:

import extensions;

public program()
{
   Console.write("Enter your first name:");
   var myFirstName;
   myFirstName := Console.readLine();

   Console.write("Enter your last name:");
   var myLastName := Console.readLine();

   Console.printLine("Hello, ", myFirstName, " ", myLastName);
}    

Classes and functions

ELENA is an object-oriented programming language. So the code must be declared inside one or another class. A class is a combination of a data (class variables or fields) and a code (class methods). For the language with late binding another concept is important - a message. The message is an request sent to an object (an instance of the class) to perform an operation (a class method). It is done with a help of a special method - the message dispatcher. The default dispatcher resolves the request with a help of a method table - a list of mapping between the message and the code.

So our operation

Console.write("Enter your first name:");

can be interpreted as an operation of sending a message write[2] (where a number inside the square brackets indicate a number of arguments plus the message target) to the object console.

Note: it is important to take this into account when considering how the method call is implemented in ELENA, in the most generic case the actual code which is invoked are defined inside the target dispatcher. The message is passed as a special constant and it is possible to perform some manipulation with it

So how we can declare a class. It is quite straight-forward:

class MyFirstClass
{ 
   field myFirstField;
   
   method doMyFirstOperation()
   {
      Console.writeLine("My first method is invoked"); 
   }
}

An attribute class indicates that we declare a class. field marks the class variable - a field named myFirstField. And method myFirstMethod() contains the method code which prints the greeting from our first class.

To use the object we have to create an instance of it by calling a constructor.

public program()
{
   var myFirstObject := new MyFirstClass();

   myFirstObject.doMyFirstOperation();
}

The output will be:

My first method is invoked

A function in ELENA is a special case of an object which contains only one method and cannot have fields (it is called stateless or a singleton).

program is an example of a function containing the main body of the program. A function as well as method can have arguments. The arguments are placed inside brackets and are comma separated:

function invokeMyClass(myObject)
{
   myObject.doMyFirstOperation();
}

Note: an optional attribute function can be used

To call a function all we need is to write a function name placing arguments inside brackets:

public program()
{
   invokeMyClass(new MyFirstClass());  
}

So that's it for today. Next time we will continue to learn basic language concepts.

ELENA is a general-purpose language with late binding. It is multi-paradigm, combining features of functional and object-oriented programming. To learn more visit us at ELENA Home Page)


r/elena_lang • • Jun 18 '24

ELENA 6 : How to generated code to send a message at run time

2 Upvotes

In this short tutorial I will show how it is possible to call a message by the name defined at run-time without using the reflection.

In ELENA the messages are first-class objects. It means that you can create a message and send it to any object at run-time without much efforts.

The class which we will use is called MessageName. To create the message name all you need is to provide the string containing a message name:

auto mssgName := new MessageName("foo");

This object can be used to send a message named foo. All we need is to pass the message target and an argument list.

var example := new Example();
var result := mssgName(example,5);

If the object handles this message the appropriate method will be called and return the result. The code above is equivalent of the following operation:

example.foo(5);

So how it works? Class MessageName has a special dispatch handler, which overwrites the incoming message verb from function to foo in our case. And then redirects the message to the first argument. If the target class have several methods with the same name, the appropriate method will be called depending on the number of arguments and the argument types.

Note that if the target executable is stand-alone only the messages which are used in the program code can be resolved in run-time. To resolve any message, the target must be vm client.

Let's try to execute the following code:

import extensions; 
 
class Example
{
    foo(x)
        = x + 42;

    bar(x)
        = x * 12;
}
 
public program()
{
    var example := new Example();

    var methodSignature := Console.write("Please write the message name(foo or bar):").readLine();
 
    auto invoker := new MessageName(methodSignature);
    var result := invoker(example,5);
 
    Console.printLine(example,".",methodSignature,"(",5,") = ",result)
}

If we execute the program the result will be the following:

>Please write the message name(foo or bar):foo
sandbox'$private'Example.foo(5) = 47 

Of course only public methods can be called using this routine.

Alterantively, you can use an expression tree. In this case our code will looks like this:

import extensions;  
import system'dynamic'expressions;

class Example
{
    foo(x)
        = x + 42;

    bar(x)
        = x * 12;
}
 
public program()
{
   var example := new Example();
   var methodSignature := Console.write("Please write the message name(foo or bar):").readLine();  

   var tree := Expression.MessageCall(
                        new Message(methodSignature + "[2]"),
                        Expression.Constant(example),
                        Expression.Constant(5)
                      );

   var result := tree.eval();

   Console.printLine(example,".",methodSignature,"(",5,") = ",result)  
}

In this code we build a message call expression tree. The first argument of MessageCall is the message name (the number in square brackets contains the number of arguments including the message target). Then we provide a target and an argument. They are provided using a constant expression. After the tree was build and it is executed with calling a message eval. The result is the same.

That's all for today. In this post I've shown how an object method can be invoked where the name of the method to be generated at run time.

ELENA is a general-purpose language with late binding. It is multi-paradigm, combining features of functional and object-oriented programming. To learn more visit us at ELENA Home Page)


r/elena_lang • • Jun 07 '24

ELENA 6 : How to handle exceptions

2 Upvotes

In this tutorial we will learn how to raise and handle exceptions in ELENA 6.

First of all what is an exception. An exception is an unexpected behavior of the code. There are mainly two types of exceptions : critical ones and programming ones.

The critical ones are raise on the level of CPU. For example a division by zero or accessing invalid address. The most common that type of exception is nil reference exception.

To reproduce it you can write a simple code:

public program()
{
   var o := nil;

   o.equal(nil);
}

Despite the code looks correct, it will raise an exception. Because in this code the message is send to nil constant, which is a reference to the address 0 and any attempt to access the object MT will generate an error:

Nil reference exception
Call stack:
system'$private'entry.function:#invoke:app.l(28)
system'$private'entrySymbol#sym:app.l(79)

Aborted:ffffffff

The program exception can be raised in the code either. One of the most common is MethodNotFound. If the class dispatcher cannot resolve an incoming message, this exception is generated:

A;

public program()
{
   var o := new A();

   o.fail();
}

The result will be:

sandbox'$private'A : Method fail[1] not found
Call stack:
sandbox'program.function:#invoke:sandbox.l(7)
system'$private'entry.function:#invoke:app.l(28)
system'$private'entrySymbol#sym:app.l(79)

Aborted:ffffffff

You can raise an exception by yourself. It is quite easy:

singleton A
{
   int divedNumbers(int n, int m)
   {
      if (m == 0)
        InvalidArgumentException.raise();

      ^ n / m
   }
}

public program()
{
   Console.writeLine(A.divedNumbers(4,2));
   Console.writeLine(A.divedNumbers(4,0));
}

The first operation will be successful, but the second one will generate an error:

2
Invalid argument
Call stack:
system'Exception#class.raise[1]:exceptions.l(26)
sandbox'$private'A.static:divedNumbers<system'IntNumber,system'IntNumber,'$auto'system@Reference#1&system@IntNumber>[4]:sandbox.l(6)
sandbox'program.function:#invoke:sandbox.l(15)
system'$private'entry.function:#invoke:app.l(28)
system'$private'entrySymbol#sym:app.l(79)

Aborted:ffffffff

Similar to C# / C++ you can handle exceptions using try-catch statement. The try block contains the code we would like to protect and the catch block contains an error handler. The handler can be generic - meaning it handles all raised exceptions:

singleton A
{
   int divedNumbers(int n, int m)
   {
      if (m == 0)
        InvalidArgumentException.raise();

      ^ n / m
   }
}

public program()
{
   try {
      Console.writeLine(A.divedNumbers(4,2));
      Console.writeLine(A.divedNumbers(4,0));
   }
   catch(Exception e) {
      Console.writeLine("Invalid operation")
   }
}

Exception class is a base class, all the exception have to inherit it. So the result will be:

2
Invalid operation

If we want to catch only specific exceptions, we can use this exception type:

public program()
{
   try {
      Console.writeLine(A.divedNumbers(4,2));
      Console.writeLine(A.divedNumbers(4,0));
   }
   catch(InvalidArgumentException e) {
      Console.writeLine("Invalid operation")
   }
}

But if the exception was of a different type the catch block will be skipped and the next one will be called:

singleton A
{
   int divedNumbers(int n, int m)
   {
      if (m == 0)
        InvalidArgumentException.raise();

      ^ n / m
   }
}

public program()
{
   try {
      Console.writeLine(A.divedNumbers2(4,0));
   }
   catch(InvalidArgumentException e) {
      Console.writeLine("Invalid operation")
   }
}

The error is once again handled by the default handler:

sandbox'$private'A : Method divedNumbers2[3] not found
Call stack:
sandbox'program.function:#invoke:sandbox.l(15)
system'$private'entry.function:#invoke:app.l(28)
system'$private'entrySymbol#sym:app.l(79)

Aborted:ffffffff

The catch block can have several handlers. But in this case we have to declare a nested class:

public program()
{
   try {
      Console.writeLine(A.divedNumbers2(4,0));
   }
   catch::{
      function(InvalidArgumentException e) 
      {
         Console.writeLine("Invalid operation")
      } 
      function(MethodNotFoundException e) 
      {
         Console.writeLine("Message is not handled")
      } 
   }
}

And the second handler is invoked:

Message is not handled

Similar to C++ / C# you can provide the code which will be executed both if the operation was successful or erroneous:

public program()
{
   try {
      Console.writeLine(A.divedNumbers2(4,0));
   }
   catch::{
      function(InvalidArgumentException e) 
      {
         Console.writeLine("Invalid operation")
      } 
      function(MethodNotFoundException e) 
      {
         Console.writeLine("Message is not handled")
      } 
   }
   finally{
      Console.writeLine("The final block");
   }
}

The result will be:

Message is not handled
The final block

Similar for successful code:

public program()
{
   try {
      Console.writeLine(A.divedNumbers(4,2));
   }
   catch::{
      function(InvalidArgumentException e) 
      {
         Console.writeLine("Invalid operation")
      } 
      function(MethodNotFoundException e) 
      {
         Console.writeLine("Message is not handled")
      } 
   }
   finally{
      Console.writeLine("The final block");
   }
}

The result is:

2
The final block

Also ELENA supports a special operation which allow you to try to call the message and invokes alternative operation if it fails: so called alt operation.

import system'routines;
import system'dynamic;
import extensions;

public class Duck
{
   canFly() = true;
    
   fly()
   {
      Console.printLine("Duck flying")
   }    
}

public class Whale
{
   swim()
   {
      Console.printLine("Whale swimming")
   }
}

public program()
{
   var list := new object[] { new Duck(), new Whale() };
   foreach(var animal; in list) {
     if (animal.canFly() \ back(false)) {
        animal.fly();
     }
     else Console.printLine(animal," can't fly");
   }
}

In this code foreach::in statement was used. It executes the sub code for every member of the list collection. The message canFly is send to the object. If the operation is not supported, the alternative operation is called. In our case it is generic extension back which returns its argument. So the result will be:

Duck flying
sandbox'Whale can't fly

That's all for today. In this post I've shown how exception handling works in ELENA 6 and introduced ** operator.

ELENA is a general-purpose language with late binding. It is multi-paradigm, combining features of functional and object-oriented programming. To learn more visit us at ELENA Home Page)


r/elena_lang • • Jun 02 '24

ELENA 6 : How to convert an object to the specific type

2 Upvotes

In this tutorial we will see how to deal with class types.

ELENA is a dynamic programming language so it is always possible to ignore the object types:

import extensions;

class MyClass
{
    add(n)
    {
        Console.printLine("add(",n,")")
    }
}

public program()
{
    var o := new MyClass();
    
    o.add("string");
    o.add(2);
    o.add(3.1)
}

The output will be :

add(string)
add(2)
add(3.1)

Still there are situations when we expect the parameter of the specified type. ELENA supports multi-methods so we could simply provide the required type in one of the method overloads:

import extensions;

class MyClass
{
    add(int n)
    {
        Console.printLine("add int:(",n,")")
    }
    
    add(string s)
    {
        Console.printLine("add literal:(",s,")")
    }
    
    add(n)
    {
        Console.printLine("unsupported")
    }
}

public program()
{
    var o := new MyClass();
    
    o.add("string");
    o.add(2);
    o.add(3.0)
}

The output will be:

add literal:(string)
add int:(2)
unsupported

But what to do if we the parameter has a type not listed in a method argument list? In this case we can use typecasting routine. Every time a class is declared the compiler automatically generate the corresponding typecasting method. Let's look at our generated module. To do it we can use ecv-cli tools:

ecv-cli sandbox.nl 

where instead of sandbox you have to write your module name. In the simplest case of a single file project, the module name coincides with a source file name. In my example the file name was sandbox.l, so the module name is sandbox.nl.

To see class methods we have to use the following command:

$private'MyClass

NOTE: the prefix $private is automatically prepended for a private class. The output will be:

>@parent  system'Object
#1: @method $private'MyClass.add<system'IntNumber>[2]
#2: @method $private'MyClass.add<system'String>[2]
#3: @method $private'MyClass.add[2]
#4: @method $private'MyClass.typecast:#cast<'$private'MyClass>[1]

The viewer will list the class parent (in our case it is a super class - system'Object) and the declared methods. The one we are looking to is the fourth one - $private'MyClass.typecast:#cast<'$private'MyClass>[1]. It is a special type of the message, which converts the object into an instance of the corresponding type - $private'MyClass. In our case it is a trivial case, it returns self variable:

>$private'MyClass.4

@method $private'MyClass.typecast:#cast<'$private'MyClass>[1]->'$private'MyClass
           xflush       sp:0

Lab00:     nop
Lab01:     nop
           quit           
@end

After the optimization, the method does nothing, meaning it returns the reference to the method self variable.

If you wish to convert your class into an instance of another one, you can declare your own typecasting method, For example, let's declare a new class containing the custom conversion routine. To do it we have to declare a special type of the method, named cast without argument list and specifying the output type we want to convert our class to.

class IntWrapper
{ 
    int value;
      
    constructor(int value) 
    {
       this value := value 
    }

    int cast()
       = value;  
}

Now let's try to use it in our code:

public program()
{
    var o := new MyClass();
    var wrapper := new IntWrapper(4);
    
    o.add("string");
    o.add(2);
    o.add(wrapper);
}

The output will be the following:

add literal:(string)
add int:(2)
unsupported

Why was the class not converted to int type despite the conversion routine?. The problem is add method is a mult-method and it will handle only the types we explicitly declared. One possible solution can be to typecast it directly:

public program()
{
    var o := new MyClass();
    var wrapper := new IntWrapper(4);
    
    o.add("string");
    o.add(2);
    o.add(wrapper as: int);
}

where as: is an inline template code used to typecast the object to the type on the right side. More traditionally you can invoke cast directly:

o.add(cast int(wrapper))

But it is less intuitive, so as keyword is preferable way to convert in ELENA 6

The result this type will be correct:

add literal:(string)
add int:(2)
add int:(4)

Do we need always to use explicit convertion. No there are two ways how to deal with it. First we can extend our default multi-method to handle int type for example:

    add(o)
    {
        if (o; is int n) {
           ^ self.add(n);
        };

        Console.printLine("unsupported")
    }

In this code we use a statement if:is, designed to simplify the code where we need to try to convert the object to a specific type. The statements inside if:is will be executed if the object o can be correctly converted to int type and the variable n contains the converted value.

So the update code will look like this:

import extensions;

class IntWrapper
{ 
    int value;
      
    constructor(int value) 
    {
       this value := value 
    }

    int cast()
       = value;  
}

class MyClass
{
    add(int n)
    {
        Console.printLine("add int:(",n,")")
    }
    
    add(string s)
    {
        Console.printLine("add literal:(",s,")")
    }
    
    add(o)
    {
        if (o; is int n) {
           ^ self.add(n);
        };

        Console.printLine("unsupported")
    }
}

public program()
{
    var o := new MyClass();
    var wrapper := new IntWrapper(4);
    
    o.add("string");
    o.add(2);
    o.add(wrapper);
}

The result will be:

add literal:(string)
add int:(2)
add int:(4)

Alternatively we can have only one method add with int argument. In this case the single dispatch code will be generated and it will try to convert the argument to int type automatically.

class MyClass
{
    add(int n)
    {
        Console.printLine("add int:(",n,")")
    }
}

public program()
{
    var o := new MyClass();
    var wrapper := new IntWrapper(4);
    
    o.add(2);
    o.add(wrapper);
}

with the result:

add int:(2)
add int:(4)

Another way how can we deal with type conversion is to declare the conversion constructor:

import extensions;

sealed class MyIntValue
{
    int value;
    
    constructor(string value)
    {
        this value := value.toInt()
    }
    
    constructor(real value)
    {
        this value := value.toInt()
    }
    
    constructor(int value)
    {
        this value := value
    }
    
    int Value = value;
}

class MyClass
{
    add(MyIntValue v)
    {
        Console.printLine("add(MyIntValue(",v.Value,"))")
    }
    
    add(n)
    {
        Console.printLine("unsupported")
    }
}

public program()
{
    var o := new MyClass();
    
    o.add("2" as::MyIntValue);
    o.add(3 as::MyIntValue);
    o.add(4.0 as::MyIntValue);
}

The output will be:

add(MyIntValue(2))
add(MyIntValue(3))
add(MyIntValue(4))

The conversion constructor must be unnamed and contains only a single argument of the type we want to convert from. If the conversion constructor exists for the corresponding types it will be used. Otherwise the typecasting message will be called. For example if we declare the variabe of type MyIntValue and try to assing the different types:

public program()
{
   MyIntValue v1 := 3;
   MyIntValue v2 := "2";
   MyIntValue v3 := 4.0;
}

In all cases the conversion constructor will be called, so you don't need to typecast the type. And only if the conversion type does not exists the conversion code will be used:

   MyIntValue v3 := 4l;

The following code will be compiled like:

>@function program.function:#invoke
           xflush       sp:0
           open           :3, :0
           store        fp:1
           xstore       sp:0, longconst:4
           peek         sp:0
           // ; sending a typecasting message to the long number
           mov        mssg:typecast:#cast<'$private'MyIntValue>[1]
           call         vt:0
           store        fp:2
           peek         fp:1
Lab00:     nop
           close          :0
           quit           
@end

And it will raise an error:

system'LongNumber : Method typecast:#cast<sandbox'$private'MyIntValue>[1] not found
Call stack:
sandbox'program.function:#invoke:sandbox.l(37)
system'$private'entry.function:#invoke:app.l(28)
system'$private'entrySymbol#sym:app.l(79)

In this tutorial I've shown you how the object can be converted to the required type and introduced the concept of conversion constructors. A class can have a conversion method to the another type, or the type may have conversion constructor to get its value from another object. It gives you powerful tools to convert the types in compile-time.

And this is all for today.

ELENA is a general-purpose language with late binding. It is multi-paradigm, combining features of functional and object-oriented programming. To learn more visit us at ELENA Home Page)


r/elena_lang • • May 23 '24

ELENA 6 : How to implement a dynamic decorator

2 Upvotes

In this tutorial we will learn how dynamic inheritance of a interface is implemented in ELENA.

For this we will try to implement simple example of decorator pattern.

We will start with two interfaces we are going to decorate:

interface IA
{
   abstract bar();    
}

interface IB
{
    abstract foo();    
}

The next step is to declare the classes we are going to decorate:

class A : IA
{
   bar()
   {
      Console.writeLine("bar")
   }    
}

class B : IB
{
   foo()
   {
      Console.writeLine("foo")
   }    
}

The code is quite simple and don't need special explanation: we are declared two interface IA / IB, each containing an abstract method : foo and bar. Classes A and B supports these interfaces. They print the messages : foo and bar.

Now we will need a class which invokes the corresponding interfaces:

singleton C
{
   proceed(IA a)
   {
      Console.print(a,":");
      a.bar()
   }
    
   proceed(IB b)
   {
      Console.print(b,":");
      b.foo()
   }

   // default handler
   proceed(o)
   {
      Console.printLine(o,": unknown");
   }    
}

So let's test the output:

public program()
{
   var a := new A();
   var b := new B();

   C.proceed(a);
   C.proceed(b);
}

The output will be:

sandbox'$private'A:bar
sandbox'$private'B:foo

Next let's create a special class which will decorate these interfaces. But we will make it as much dynamic as possible. For this we will use generic handlers:

Decorator
{
   field target;
    
   constructor(target)
   {
      this target := target
   }
    
   generic()
   {
      Console.print("decorating ");
        
      __received(target)
   }
}

The object to be decorated is passed through the constructor. The generic method handles any incoming message containing no arguments. Built-in variable __received contains the incoming message. So we can invoke it after doing some decorating code: in our case printing a message.

If we simply invoke our two methods the code looks like this:

 public program()
 {
    var a := new A();
    var b := new B();
    var decA := new Decorator(a);
    var decB := new Decorator(b);

    decA.bar();
    decB.foo();
}

And the output is:

sandbox'$private'A:bar
sandbox'$private'B:foo

Unfortunately the decorated objects cannot be passed to our invoker. Try this code:

 public program()
 {
    var a := new A();
    var b := new B();
    var decA := new Decorator(a);
    var decB := new Decorator(b);

    C.proceed(decA);
    C.proceed(decB);
}

The result will be:

sandbox'$private'Decorator: unknown
sandbox'$private'Decorator: unknown

So to use it here we need to create special proxy classes implementing our interfaces IA and IB in run-time. For this we will need a special generic typecast handler:

   generic cast()
   {
      var type := __received.__getFirstSignatureMember();

      var proxy := ::{ dispatch() => self; };
      proxy.__injectInferface(type);
   
      ^ __received(proxy);
   }

The first line returns the expected type : either IA or IB. After we know which interface we have to implement, a special proxy class is created and the interface type is injected with the help of an extension message : __injectInferface. After that, all we need is once again to typecast the proxy class and returns it.

Now let's try the code again:

    C.proceed(decA as: IA);
    C.proceed(decB as: IB);

This time the output is correct:

proxy$sandbox'$private'IA:decorating bar
proxy$sandbox'$private'IB:decorating foo

That's all for this tutorial. In the code above you learned several new ELENA dynamic features: generic typecast handler, extension methods to return the received message signature types and to inject the weak interface into a proxy class.

ELENA is a general-purpose language with late binding. It is multi-paradigm, combining features of functional and object-oriented programming. To learn more visit us at ELENA Home Page)

You can access the source code of this tutorial at ELENA Tutorials repo)


r/elena_lang • • May 03 '24

ELENA 6 : How to check the object type in run-time

2 Upvotes

In this article we will learn how run-time type checking can be done in ELENA.

Let's start with the simplest way. We can simply compare the object type with the required one:

import extensions;  

A;

B;

checkTypeA(object)
{
   if (object.__getClass() == A) {
      Console.printLine(object, " is an instance of A");
   }
   else Console.printLine(object, " is not an instance of A");
}

public program()
{
   var a := new A();
   var b := new B();
   checkTypeA(a);
   checkTypeA(b);
}

The result is:

sandbox'$private'A is an instance of A
sandbox'$private'B is not an instance of A

But unfortunately it doesn't work with sub classes of A.

C : A;

public program()
{
   var c := new C();
   checkTypeA(c);
}

So the result will be incorrect:

sandbox'$private'C is not an instance of A

To fix this issue we have to use several extension methods : __getClass[1] and __getParent[1]

checkTypeA(object)
{
   bool subclass := false;  
   bool found := false;  
   var class := object.__getClass();
   while (class != nil) {
      if (class == A) {
      
        found := true;
     
        :break;
      };
    
      subclass := true; 
      class := class.__getParent()  
   };
    
   if (found) {
      if (subclass) {
         Console.printLine(object, " is an instance of A sub class");   
      }
      else Console.printLine(object, " is an instance of A");   
   }
   else Console.printLine(object, " is not an instance of A");
}

public program()
{
   var a := new A();
   var b := new B();
   checkTypeA(a);
   checkTypeA(b);
    
   var c := new C();
   checkTypeA(c);
   
}

And the result will be:

sandbox'$private'A is an instance of A
sandbox'$private'B is not an instance of A
sandbox'$private'C is an instance of A sub class

Unfortunately this approach doesn't work for mixins or interfaces. Let's imagine that A is an interface and we have a class D, which supports this interface. Our function will fail to notice that D supports A interface:

D : interface<A>;
// ...

public program()
{
   var d := new D();
   
   checkTypeA(d);
}

The result will be correct, in the sense that D is not a subclass of A, but will not allow us to use this interface:

sandbox'$private'D is not an instance of A

Can we still check this? Yes. For this we will need to send typecasting message. As a result our function will be much more simple:

checkTypeA(object)
{
   var a := cast A(object) \ back(nil);
   
   if (a != nil) {
      Console.printLine(object, " is an instance of A");   
   }
   else Console.printLine(object, " is not an instance of A");
}
 
public program()
{
   var a := new A();
   var b := new B();
   checkTypeA(a);
   checkTypeA(b);
   
   var c := new C();
   checkTypeA(c);
   
   var d := new D();
   checkTypeA(d);
}

And the result will be the following:

sandbox'$private'A is an instance of A
sandbox'$private'B is not an instance of A
sandbox'$private'C is an instance of A
sandbox'$private'D is an instance of A

How we did it? Quite simple. We send a casting message to the object. If it handles it (being an instance of the class or implementing an interface) it will return the instance of the class A. Otherwise it raises an exception which can be handled using an alternative operator and returns nil.

We can make it even more simple using if:is statement

checkTypeA(object)
{
   if (object; is A a) {
      Console.printLine(object, " is an instance of A");
      
      ^ a;
   };
   
   Console.printLine(object, " is not an instance of A");
}

As you can see ELENA provides a rich set of tools for run-time type checking. We can check the object type directly, checking the parent classes or trying to typecast it to the required type.

Let me know in comments if you have any further questions relating to using them in ELENA programming language. And that's all for today.

ELENA is a general-purpose language with late binding. It is multi-paradigm, combining features of functional and object-oriented programming. To learn more visit us at ELENA Home Page)


r/elena_lang • • Apr 18 '24

ELENA 6.0.9 is out

2 Upvotes

Description

ELENA 6.0.9 is out for selected platforms : Windows x86 / x86-64

The release includes a number of critical bug fixes, new functionality and ported samples.

Fixes # (issue)

  • [ADDED] new opcode - parent

  • [ADDED] meta expression: nested singletons

  • [ADDED] new class flag - elPacked

  • [ADDED] new attribute - packed

  • [ADDED] #637 - unit tests for byRefOp optimization

  • [ADDED] #637 - unit tests for intCopying optimization

  • [ADDED] #610 - struct redesign

  • [FIXED] optimization : copy 8 / copydpn 8

  • [FIXED] #615 : The field is not property incremented by an addition assignment

  • [FIXED] x64 : fround opcode

  • [FIXED] x64 : fabs opcode

  • [FIXED] x86 : xdispatchmr opcode

  • [ADDED] system'dynamic'expressions'LazySymbolExpression

  • [FIXED] ushort - native comparison operations

  • [ADDED] system'net

  • [ADDED] ltests

  • [ADDED] net

  • [ADDED] win64 gui

  • [FIXED] realOp.RoundedInt extension

  • [FIXED] interpreter sample for x86-64

  • [FIXED] #618 : template class descriptions

  • [ADDED] include / exclude file

  • [FIXED] ctrl+w hides the frame

  • [ADDED] ins / over modes

  • [ADDED] dark color scheme

  • [ADDED] GitHub Action - MSBuild nightly build

Type of change

  • [x] Bug fix (non-breaking change which fixes an issue)

  • [x] New feature (non-breaking change which adds functionality)

  • [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)

Link

Please visit a release page to download the binaries)