Click here to Skip to main content
13,800,314 members
Click here to Skip to main content
Add your own
alternative version

Stats

64.1K views
621 downloads
45 bookmarked
Posted 12 Sep 2016
Licenced Apache

Introduction to ELENA Programming Language

Rate this:
Please Sign up or sign in to vote.
ELENA is a general-purpose, object-oriented, polymorphic language with late binding

Introduction

ELENA is a general-purpose, object-oriented, polymorphic language with late binding. It features message dispatching/manipulation, dynamic object mutation, a script engine / interpreter and group object support.

 In this article I will present the basic concepts of how to code in ELENA and also will present the basic concepts of purely object-oriented programming.

Features of ELENA language

  • Pure polymorphic object oriented language
  • Dynamic "class mutation"
  • Message dispatching
  • Virtual machine
  • Command line 32-bit compiler
  • GUI IDE & Debugger
  • Unicode support (utf-8)
  • Complete source code
  • Number of samples
  • Script Engine / Interpreter
  • Virtual Machine Terminal

Where can I get the executable of compiler and source code?

ELENA development community

Build the ELENA compiler

The ELENA compiler was fully implemented in C++ to compile the compiler is not required external dependencies, such as Flex, Bison... you just need Visual Studio 2015/2017.

Alex implemented its own syntax generator, assembler and the tools he needed.

To build the compiler you can open the CMD, go to root elena folder and type:

  • for VS2015 : recompile.bat / for VS2017 : recompile15.bat

or, to generate release files

  • for VS2015 : vs_release.bat %cd% / for VS2017 : vs_release15.bat %cd%

Yet another programming language

"Probably every person hearing about a new language asks this question: why yet another programming language? I cannot say about all but me. When I decided 10 years ago to create a new programming language I had several reasons. First of all it was a perfect way to learn C++ (as many Russians I came from Pascal & Delphi world). But most of all I was interested in object-oriented programming. For me it was a perfect chance to learn more about it. I was fascinated with Smalltalk and decided to do something similar. Writing the compiler is a quite challenging task but it cannot be compared with the task of creating a language or a programming paradigm (after all the most valuable things in our world are ideas). So after several attempts to invent / create something original I decided to follow other way: implement something simple and gradually modify it in hope to find something interesting. So step by step the language grew (practically any part of the compiler / language was rewritten / refactored at least several times) and now it has nothing common with the one I decided at the beginning. And though many of its ideas which I thought were original are used in other languages ELENA has its own individuality and style.

So how could I answer on this question? Is ELENA yet another programming language. The answer is not simple. Yes, ELENA may be considered as another amateur language with small source code base and lack of bug tracking functionality. And no, ELENA is a conceptual language. It is designed to examine / prove feasibility of several basic concepts. From the beginning I tried to make the language as dynamic as possible. My ultimate goal is to create the programming systems with truly open architecture, the systems which can be modified / extended in run time, the system where a big number of "simple" objects actively cooperate with each other forming group objects, able to self-modify. Will the language become something more than experimental one? Only time will show. Honestly I have no idea where the language will be in the next several years. One thing I may say definitely - it will be different. " - http://elenalang.blogspot.com.br/2010/07/yet-another-programming-language.html

First steps

It is extremely important before beginning the program in a 100% object-oriented language, know the basic concepts of a truly object-oriented language.

Basic OOP Concepts and Terminology

What are the basic conceps of ELENA ? As SmallTalk, the basic concepts are:

  • Objects
  • Classes
  • Fields
  • References
  • Methods
  • Messages
  • Inheritance
  • Receiver
  • Dynamic Binding

Objects

The ELENA "object" is very similar to the .Net or C++ object, in particular, each object is stored in memory and has several fields or methods, which can point to other objects. Each object is an instance of a class.

Classes

An object is lika a C 'struct' or a Ada record, the only difference between a record and a object is that the objects contains a field that identifies which class the object belongs to. In C++, C#, ELENA or Smalltalk, there is a single hidden field in every object. In ELENA this field is called the "dispatch table reference" and, inside of "dispatch table" have the informations about class. In other languages, this hidden field is called the "V-table pointer" or "dispatch table pointer".

Fields

Fields, also known as the instance variables, are the variables declared within a class. These variables can only be read with the use of methods to assign or read the contents of the field.

References

In C++, we talk about "pointers" to objects while in C# we talk about "references" to objects. Both concepts basically the same and references are implemented by straight pointers, which are just memory address. In ELENA, we use references.

Method

Method is like a function, except that it is applied to a specific object. We also say that the method is "invoked" on an object or "sent to" an object. The object in question is called the "receiver". Every method is invoked on receiving object. In C++ or C#, the receiver is called "this", in ELENA - "self".

Methods in ELENA are similar to methods in C# and C++, where they are called "member functions". Methods may take arguments and always return a result (if no result provided "self" reference is returned). The method body is a sequence of executable statements. Methods are invoked from expression, just as in other languages.

Messages

There is an important distinction between "methods" and "messages". A method is a body of code while a message is something that is sent. A method is similar to a function. in this analogy, sending a message is similar to calling a function. An expression which invokes a method is called a "message sending expression".

ELENA terminology makes a clear distinction between "message" and "method". A message-sending expression will send a message to the object. How the object responds to the message depends on the class of the object. Objects of differents classes will respond to the same message differently, since they will invoke different methods.

The "Hello World!" application

A great start for when we are studying or just knowing any new programming language, is always the famous program "Hello world!".

In order to create our first program, we have to get the ELENA compiler, for this, you just need to download the compiler in this link.

After download and extracted all the content to the folder of your choice, 

Important: You need to put the "bin" folder in your system path.

With the bin folder in your path system, this is necessary to able the generate executable to access the elena runtime than is used for example, when a exception throws to show in what class & method the exception happens.

Let's start, first of all, we need to open the ELENA IDE, for this, you can type in CMD "elide" or, just open the executable called "elide".

After open the elide IDE, we will create a new project, for this, we just need to go to menu "File->New->Project", when you click in the project menu, will open a dialog like this:

What every field means:

  • Type: Is the type of application than you will create, like GUI, Console, Console App running over VM ....
  • The namespace of application
  • The checkbox to warning about unresolved references
  • Additional options for the compiler
  • Target file name: the output file name
  • Output path: output of ELENA objects
  • Enable or disable the debugger
  • Command line arguments to test the application

For this article we will use these specific settings:

After you click in "ok" we need to create a new source file, for this, just go to menu "File->Source file" or use the shortcut "Ctrl+N".

Now, we can start to code... this is the code for Hello World

public program
[
    system'console writeLine("Hello World!").
]

After you write this code, you need to save the project and the source code in some directory and you just need to build it.

Understanding the code

public program

Here we declare a public singleton closure (a static class containging one message). By default, the "program" is the entry point of the program, but, it's possible to change this.

system'console writeLine("Hello World!").

In this line of code, we have a very intresting informations to look at:

  1. "system": It is a namespace, the namespace itself may contain sub elements separated by apostrophes
  2. "console": Is a class
  3. "writeLine": Is a message
  4. "Hello World!": Is the parameter of the message
  5. ".": Terminator

What this mean for us ?
That means than we send a message to method writeLine of class console in namespace system with parameter "Hello World!".

Like in other languages, like C#, C++, Python we can add a special keyword to "import" some namespace, with this we can 'reduce' code.

So, the hello world example with "import" keyword will be like:

import system.

public program
[
    console writeLine("Hello World!").
]

The unique problem than we have with this program is than we can't read the output..., so, to fix this we just need to use one more method, and, we can use this method in two ways.

First:

import system.

public program
[
    console writeLine("Hello World!").
    console readChar.
]

Second:

import system.

public program
[
    console writeLine("Hello World!"); readChar
]

The last dot can be omitted.

This second way to write the same program works well because in ELENA every method returns an object.

Creating variables

Like in Smalltalk, in ELENA everything is a object, so, to create variables we just need to use a special 'keyword'

var

The syntax of this keyword is, basically:

var <variable name> := <Class instance>.

So, as example, let's write a simple program to read something from the keybord.

If you preffer, you can create a new console project for this example.

import system.
import extensions.

public program
[
    var aNumber := console write("Type a number: "); readLineTo(Integer new).
    console printLine("You typed " + aNumber).
    console readChar
 ]

Really simple, right ?
So, in this example we need to import a one more namespace, the namespace extensions contains the definition for the "readLineTo(<type>)".

A variable can contains any object types, for example:

var x := IntNumber new.
var y := 0.
var k := system'calendar'Date new.
var n := k year.
var s := "Hello World".
var u := 0fffffh.
var l := 3.1415r.
var m := RealNumber new(3.14r).
var j := true.
var h := false.

Conditional operator, if and else

Conditional operations in ELENA are defined as follows:

(<myCondition>)
? [ doSomething in case TRUE ]
! [ doSomehting in ELSE ].

It's more simple to see this, in this simple example:

import system.
import extensions. 

public program
[
    var aTrue := true.
    var aFalse := false.
    
    (aTrue && aFalse)
        ? [ console printLine("aTrue && aFalse is: ",aTrue && aFalse) ]
        ! [ console printLine("aTrue && aFalse is: ",aTrue && aFalse) ].
        
    (aTrue || aFalse)
        ? [ console printLine("aTrue || aFalse is: ",aTrue || aFalse) ]
        ! [ console printLine("aTrue || aFalse is: ",aTrue || aFalse) ].
    
    console readChar
]

Template based if-else condition

To simplify the code, we may use a code template - if. Let's take a look:

var a1 := true.
var a2 := false.

 if(a1 == true)
 [
     console printLine("a1 == true").
 ];
 if(a2 == false) // Else if case
 [
 ];
 [ // Else case
    console printLine("a1 != true").
 ]
Let's implement an example program containing many conditions and variable statements.
import extensions.

public program
[
  console writeLine:"Hi!! :)".
  var aYourName := console writeLine:"Whats your name ?"; readLine.
  console writeLine:"How old are you ?".
  var aYourAge := console readLineTo(Integer new).
 
  var aNumberOfChildrens := 0.
  if((aYourAge >= 0) && (aYourAge <= 10))
  [
    console writeLine:"You're still very young!!"
  ];
  if((aYourAge >= 11) && (aYourAge <= 20))
  [
    console writeLine:"Adolescence, come a lot of people angry?"
  ];
  if((aYourAge >= 21) && (aYourAge <= 30))
  [
    console writeLine:"Do you already have children ? ( yes/no )".
    var aHaveChildren := console readLine.
    if(aHaveChildren lowerCase == "yes")
    [
      aNumberOfChildrens := console writeLine:"Cool!! how many kids do you have ?";0
                                     readLineTo(Integer new)
    ].
  ];
  if((aYourAge >= 31) && (aYourAge <= 40))
  [
    console writeLine:"The first 40 years of childhood are the most difficult !!!"
  ];
  if((aYourAge >= 41) && (aYourAge <= 50))
  [
    console writeLine:"Life begins at 40!"
  ];
  if((aYourAge >= 51) && (aYourAge <= 60))
  [
    console writeLine:"Enjoy your LIFE !!!"
  ];
  if(aYourAge >= 61)
  [
    console writeLine:"You are at the age to enjoy and relax !!"
  ].
  console readChar
]

Loops

Like in other programming languages, ELENA have a support for loops, let's take a look at the while loop, this loop is implemented using a template while.

while(<condition>)
[ /* ... loop code... */ ].

So, to make clear the usage of loop, let's write a simple program to:

import system.
import extensions. 

public program
[
    var aArray := Array new:200.
    
    var i := 0.
    while(i < aArray length)
    [
       aArray[i] := i * 10.
       i := i + 1
    ].
      
    i := 0.
    while (i < aArray length)
    [
      console printLine("At index ", i, " has the value:", aArray[i]).
      i := i + 1
    ].
    
    console readChar
]

Classes and Methods and Fields

"ELENA is an object-oriented language. To create a program we have to declare new classes.

A class encapsulates data (fields) with code (methods) to access it. In most cases it is not possible to get a direct access to the class content. Usually the field refers to another class and so on until we reach "primitive" ones which content are considered as raw data (e.g. numeric or literal values).

Classes form the inheritance tree. There is the common super class - system'Object. ELENA does not support multiple inheritance, though it is possible to inherit the code using a dispatch handler (mixins / group objects). When the parent is not provided the class inherits directly system'Object (the super class).

A class instance can be created with the help of the special methods - constructors. A constructor is used mostly to initialize the class fields. There are special types of classes which do not have constructors and can be used directly (nested classes, extensions, closures). A class itself is considered as a stateless object." - https://github.com/ELENA-LANG/elena-lang/wiki/ELENA-Programming-Language#13-classes-symbols-nested-classes-and-closures

To understand this information, we will write some classes, to make clearer its implementation and use.

You can create a new project to create the classes that will create as example.

For the first example, we will create a simple class.

import system.
import extensions.

class MyFirstClass
{
    constructor new
    [
        console printLine("MyFirstClass instancied!!! ehhhhhhh!!").
    ]
}

public program
[
    var myFirstClass := MyFirstClass new.
    console readChar
]

So, when you execute the program you will see the message on console:

MyFirstClass instancied!!! ehhhhhhh!!

Really simple, right ? (:

So, now that we know how to create a class, we will add methods in this class and we will create another constructor for this class, passing two arguments, those two parameters will be saved in two different fields...

First of all, we will create the new constructor with the new parameters, the class will look like:

import system.
import extensions.

class MyFirstClass
{
    int field1.
    int field2.
    
    constructor new
    [
        console printLine("MyFirstClass instancied!!! ehhhhhhh!!").
    ]
    
    constructor new(int aParameter1, int aParameter2)
    [
        console printLine("MyFirstClass instancied!!! with 2 parameters!!").
        console printLine("My first parameter.: ", aParameter1).
        console printLine("My second parameter: ", aParameter2).
        
        field1 := aParameter1.
        field2 := aParameter2.
    ]
}

public program
[
    var myFirstClass := MyFirstClass new.
    var myFirstClassWith2Parameters := MyFirstClass new(500, 300).
    console readChar
]

Notice that in program, we created a new variable to store the new instance of the class, which is initialized with two parameters.

Now let's implement some methods to work with these two values that were passed by parameter.

class MyFirstClass
{
    int field1.
    int field2.
    
    constructor new
    [
        console printLine("MyFirstClass instancied!!! ehhhhhhh!!").
        
        field1 := 0.
        field2 := 0.
    ]
    
    constructor new(int aParameter1, int aParameter2)
    [
        console printLine("MyFirstClass instancied!!! with 2 parameters!!").
        console printLine("My first parameter.: ", aParameter1).
        console printLine("My second parameter: ", aParameter2).
        
        field1 := aParameter1.
        field2 := aParameter2.
    ]
    
    addTwoNumbers
    [
        ^ field1 + field2.
    ]
    
    addTwoNumbers(int aNumber1, int aNumber2)
    [
        ^ aNumber1 + aNumber2.
    ]
    
    addTwoNumbersAndPrintIt
    [
        var addResult := self addTwoNumbers.
        console printLine("Result of ", field1, "+", field2, "=", addResult).
    ]
    
    addTwoNumbersAndPrintIt(int aNumber1Argument, int aNumber2Argument)
    [
        var addResult := self addTwoNumbers(aNumber1Argument, aNumber2Argument).
        console printLine(Result of ", aNumber1Argument,"+",aNumber2Argument,"=",addResult).
    ]
}

Ok, now we have 4 methods, and we want to use this methods. Using the methods have no secret, we are using methods and classes that begin to write the codes in Elena for this article!! (:

So, the usage sample of this class is:

public program
[    
    var myFirstClass := MyFirstClass new.
    var myFirstClassWith2Parameters := MyFirstClass new(500, 200).
    
    // Working just with the myFirstClassInstance
    myFirstClass addTwoNumbersAndPrintIt.
    myFirstClass addTwoNumbersAndPrintIt(20, 20).
    
    console writeLine:"---------------------------------------".
    
    // Working just with the myFirstClassWith2Parameters
    myFirstClassWith2Parameters addTwoNumbersAndPrintIt.
    myFirstClassWith2Parameters addTwoNumbersAndPrintIt(15, 320).
    
    console readChar
]

One of the basic principles of object-oriented programming is inheritance between classes, so let's implement two classes, a base class and a derived class.

import system.
import extensions.

class MyFirstBaseClass
{
    literal theField1.
    literal theField2.
    
    constructor new
    [
        theField1 := "".
        theField2 := "".
    ]
    
    constructor new(literal aField1)
    [
        console printLine("fieldBaseClass1 = ", aField1).
        
        theField1 := aField1.
        theField2 := "".
    ]
    
    constructor new(literal aField1, literal aField2)
    [
        console printLine("fieldBaseClass1 = ", aField1).
        console printLine("fieldBaseClass2 = ", aField2).
        
        theField1 := aField1.
        theField2 := aField2.
    ]
    
    printFields
    [
        console printLine(self field1).
        console printLine(self field2).
    ]
    
    field1 = theField1.
    field2 = theField2.
}

class MyFirstDerivedClass :: MyFirstBaseClass
{
    constructor new
    [
        console writeLine:"Constructor of MyFirstDerivedClass called with no parameters".
    ]
    
    constructor new(literal aField1)
        <= new(aField1 + " -- Called from DerivedClass");
    [
        console printLine("Constructor of MyFirstDerivedClass called with 1 parameters").
        console printLine("field1 = ", aField1).
    ]
    
    constructor new(literal aField1, literal aField2)
        <= new(aField1 + " -- Called from DerivedClass", 
               aField2 + " -- Called from DerivedClass");
    [
        console printLine("Constructor of MyFirstDerivedClass called with 2 parameters").
        console printLine("field1 = ", aField1).
        console printLine("field2 = ", aField2).
    ]
}

public program
[
    var myFirstDerivedClassInstance := nil.
    
    var numberOfArgsToInitInInstance := 0.
    var continueLoop := true.
    while(continueLoop)
    [
        numberOfArgsToInitInInstance :=
            console printLine("Enter with how much variables you want to use in instance init ? (0/1/2)"); 
                    readLineTo(Integer new).
            
        if((numberOfArgsToInitInInstance >= 0) && (numberOfArgsToInitInInstance <= 2))
           [ continueLoop := false ].
    ].
    
    // This is a 'swith'...
    numberOfArgsToInitInInstance =>
        0 [ myFirstDerivedClassInstance := MyFirstDerivedClass new ];
        1 [ myFirstDerivedClassInstance := MyFirstDerivedClass new("Argument1 from program") ];
        2 [ myFirstDerivedClassInstance := MyFirstDerivedClass new("Argument1 from program", 
                                                                     "Argument2 from program") ];
        ! [ console writeLine:"invalid option..." ].
          
    myFirstDerivedClassInstance printFields.
    
    console readChar
]

It is possible to declare the private methods which cannot be called outside the class.

import extensions.

class MyClass
{
    private printPrivate
    [
        console printLine("private print.")
    ]
    
    printPublic
    [
        console print("Calling from public print - ").

        // $self is a reference to the current object
        self printPrivate.
    ]
}

public program =
[
    // Note that if the constructor explicitly is not declared 
    // the system'Object one (without input parameters) is inherited
    var myClass := MyClass new.
    
    myClass printPublic.
    myClass printPrivate.
].

The output is:

Calling from public print - private print.
mytest'MyClass : Method mytest#printPrivate not found
Call stack:
system'Exception#class.new$system'LiteralValue[1]:exceptions.l(125)
system'MethodNotFoundException#class.new$system'Object$system'Message[2]:exceptions.l(213)
system'#inline1BF.start[1]:win32_app.l(252)
mytest'program.#invoke[0]:test.l(24)
system'BaseFunction.eval[0]:control.l(172)
system'#inline1BF.start[1]:win32_app.l(37)
system'startUp(5)

In normal case the class fields cannot be accessed outside the class. That's why we may declare a special method to access it:

import extensions.

class MyClass
{
    int _x.

    int x = _x.  // get accessor

    set x(int o) // set accessor 
    [
       _x := o.
    ]
}

public program
[
    var myClass := MyClass new.

    myClass x := 2.

    console printLine("MyClass.x=", myClass x)
]

We may simplify our code if we will use prop field template:

import extensions.

class MyClass
{
    int prop x :: _x.
}

public program =
[
    var myClass := MyClass new.

    myClass x := 2.

    console printLine("MyClass.x=", myClass x).    
].

Output in both cases will be:

MyClass.x=2

Exception Handling

We may use try-catch statement to handle the possible exceptions:

import extensions.
import system'io.

public program =
[
    try(console printLine(File new("notexistingfile.txt"); content))
    {
        on(Exception ex)
        [
            console printLine("Unknown error - ",ex).
        ]
        
        on(IOException ex)
        [
            console printLine("I/O error - ",ex).
        ]            
    }
].

The output is :

I/O error - Cannot open the file
Call stack:
system'Exception#class.new$system'LiteralValue[1]:exceptions.l(125)
system'io'IOException#class.new$system'LiteralValue[1]:io\ioexceptions.l(10)system'io'FileStream#class.new$system'WideLiteralValue$system'IntNumber$system'IntNumber$system'IntNumber$system'IntNmber[5]:io\win32_files.l(52)
system'io'FileStream#class.openForRead[1]:io\win32_files.l(29)
system'io'StreamReader#class.new[1]:io\win32_files.l(207)
system'io'fileControl.reader[1]:io\win32_files.l(269)
system'io'File.read$system'text'TextBuilder[1]:io\files.l(59)
system'io'File.content[0]:io\files.l(33)
mytest'program.#invoke[0]:test.l(6)
system'BaseFunction.eval[0]:control.l(172)
system'#inline1BF.start[1]:win32_app.l(37)
system'startUp(5)

Working with enumerable objects

ELENA supports several different types of enumerable objects : arrays, lists, dictionaries, ranges and so on.

To be used as enumerable one, object should handle enumerator message.

Let's consider a simple example:

import extensions.

public program
[
    var list := (1,2.3r,"String",3l).
    
    var it := list enumerator.
    while (it next)
    [
        console printLine(it get).
    ]
]

In the output every member of collection will be printed:

1
2.3
String
3

In this code we declare a static array - list. We send enumerator message to it and assign the returned enumerator to the variable it. Then we repeat the code until next message returns false value. get message returns a current enumeration member.

The same pattern can be applied for any enumerable object:

import extensions.
import system'routines.

public program
[
    var range := Range from:1 till:10.
    
    var it := range enumerator.
    while (it next)
    [
        console printLine(it get).
    ]
]

Here we generate and print a range of natural numbers from 1 till 10. The result is:

1
2
3
4
5
6
7
8
9

Similar to C# there are a lot of extension methods which can be used for searching, counting, filtering and so on.

Let's start with a simple one - forEach - executes a code for each enumeration member. The code above can be rewritten using forEach extension method (declared in system'routines module) and a closure:

import extensions.
import system'routines.

public program =
[
   Range from:1 till:10; forEach(:item)[ console printLine(item) ].
].

where (:item)[ <...> ] is a general closure with a single parameter.

We may further simplify our code using existing closure extensions'routines'printingLn

import extensions.
import system'routines.
import extensions'routines.

public program
[
    (1,2.3r,"String",3l) forEach:printingLn.
]

In both cases the output will be similar to our first two examples.

We may combine several extension methods in a row. For example filterBy is used to filter an enumeration based on the parameter output. Only the members for which filter function returns true will be passed further. It may be used in combination with other extension methods like forEach.

import extensions.
import system'routines.
import extensions'routines.
import system'math.

public program
[
    Range from:1 till:10; filterBy(:i)(i mod:2 == 0); forEach:printingLn.
]

filterBy will return only even numbers. The output will be:

2
4
6
8

Note that (:item)( <...> ) is a lambda closure which is shortcut form of (:item)[ ^<...> ]

summarize extension is used to summarize all the members of the collection:

import extensions.
import system'routines.
import extensions'routines.
import system'math.

public program
[
    console printLine(Range from:1 till:10; filterBy(:i)(i mod:2 == 0); summarize).
]

The result will be a sum of first 10 even natural numbers:

20

Using toArray extension we may save our enumeration as an array:

import extensions.
import system'routines.
import extensions'routines.
import system'math.

public program
[
    var evens := Range from:1 till:10; filterBy(:i)(i mod:2 == 0); toArray.

    console printLine("sum(",evens,")=", evens summarize).
]

toArray will collect the enumeration member into the array. And the output is:

sum(2,4,6,8)=20

We may limit our output using top extension:

import extensions.
import system'routines.
import extensions'routines.
import system'math.

public program
[
    Range from:1 till:100; filterBy(:i)(i mod:2 == 0); top:10; forEach:printingLn.
]

The result will be first 10 even numbers:

2
4
6
8
10
12
14
16
18
20

We may combine several enumerable objects into a single collection using zip:by extension:

import extensions.
import system'routines.
import extensions'routines.

symbol list = ("a","b","c","d").

public program
[
    list zip:(Range from:1 to(list length)) 
        by(:ch:i)(i literal + " - " + ch literal); forEach:printingLn.
]

And the output is:

1 - a
2 - b
3 - c
4 - d

where (:ch:i)(<...>) is a lambda closure with two parameters

selectBy extension can be used generate a new collection based on previous one. orderBy extension will sort a collection:

import extensions.
import system'routines.
import extensions'routines.

public program
[
    var list := Range from:1 to:5; selectBy(:n)(randomGenerator nextInt(100)); toArray.
    
    console printLine("sort(",list,")=",list orderBy(:p:n)(p < n)).
]

The result will be a randomly generated list of numbers, ordered in assenting order:

sort(50,94,40,78,93)=40,50,78,93,94

groupBy may be used to group the enumeration members into a sub collections:

import extensions.
import system'routines.                              
import extensions'routines.

public program =
[
    var list := Range from:1 to:20; selectBy(:n)(randomGenerator nextInt(10)); toArray.
    
    list groupBy(:x)(x); 
    selectBy(:sub_list)(sub_list key; literal + ":" + sub_list countMembers; literal + " times");
    orderBy:ifOrdered;
    forEach:printingLn.
].

The code will count how many times a random number is encountered:

0:2 times
1:4 times
2:6 times
3:2 times
4:2 times
5:4 times
6:10 times
7:6 times
8:2 times
9:2 times

Dynamic dispatching, Generic handlers and Mixins

Let's start with a multi-methods. We may declare a several methods with the same name but with different signatures. It is also possible to declare an explicit multi-method dispatcher.

class MyClass
{
    // accepts the integer
    test(IntNumber n)
    [
            console writeLine:"It is a number".
    ]

    // accepts the string
    test(LiteralValue n)
    [
        console writeLine:"It is a literal".
    ]

    // default dispatcher
    test:n
    [
        console writeLine:"Unsupported parameter".
    ]
}

public program
[
    object o := MyClass new.

    o test(2).
    o test("s").
    o test(3l).
]

The output is:

It is a number
It is a literal
Unsupported parameter

In some cases opposite can be done as well, we may declare generic handlers which will accept any incoming messages:

import extensions.
 
class Example
{
    generic
    [
        // received is an built-in variable containing the incoming message name
        console printLine(received," was invoked").
    ]
 
    generic : x
    [
        console printLine(received,"(",x,") was invoked").
    ]
 
    generic(x,y)
    [
        console printLine(received,"(",x,",",y,") was invoked").
    ]
}
 
public program
[
    var o := Example new.
 
    o foo.
    o bar(1).
    o someMethod(1,2).
]

Output:

foo was invoked
bar(1) was invoked
someMethod(1,2) was invoked

We may declare a custom dispatcher which will redirect all unmapped incoming messages to another object effectively overriding it (some kind of dynamic mutation / code injection).

import extensions.

class Extender
{
    // our object to extend
    object theObject.
    
    // the injected property
    object prop foo :: theField.
    
    constructor new : anObject
    [
        theObject := anObject.
    ]
    
    // redirect Object.literal method to theObject one
    T<literal> literal => theObject.

    // custom dispatcher
    dispatch => theObject.
}

public program
[
    var anObject := 234.
  
    // adding a field
    anObject := Extender new:anObject.

    // setting a field value
    anObject foo := "bar".

    console printLine(anObject,".foo=",anObject foo).
]

The output is:

234.foo=bar

The message may be dynamically dispatched.

class MyClass
{
    eval
    [
       console writeLine:"eval method"
    ]                                                    

    state0
    [
       console writeLine:"state0 method"
    ]
}  

public program
[
   var o := MyClass new.

   var subj := %state0.  // a message name constant

   o eval.              

   o~subj eval           // dynamically dispatching the message
]

The output is:

eval method
state0 method

Though ELENA does not support multiple inheritance, using custom dispatcher we may simulate it:

class CameraFeature =
{
    cameraMsg
        = "camera".
}.
 
class MobilePhone
{
    mobileMsg
        = "phone".
}
 
class CameraPhone :: MobilePhone
{
    dispatch => CameraFeature.
}
 
public program
[
   var cp := CameraPhone new.
 
   console writeLine(cp cameraMsg).
   console writeLine(cp mobileMsg).
]

The output is:

camera
phone

Now let's create a mixin object:

import system'dynamic.
import extensions.
 
class Member1
{
    literal theField := "member1 content".

    field = theField.
}

class Member2
{
    eval
    [
        // NOTE : target is built-in variable referring to the mixin
        console printLine(
           "printing the content of the second object:",
           target field).
    ]
}

public program =
[ 
    var g := Group::(Member1 new, Member2 new).
    g eval.
].

Output is:

printing the content of the second object:member1 content

Using ClosureTape we may dynamically build the code:

import extensions.
import system'dynamic.

public program
[
    var t := ClosureTape new(MessageClosure new(%"writeLine[1]")).

    t(console,"Hello again").
]

The output is:

Hello again

The code may be simplified if we will build an expression tree:

import system'dynamic'expressions.

public program
[
    var c := Expression MessageCall(
                            %"writeLine[1]", 
                            Expression Constant(console), 
                            Expression Constant("Hello")).
    
    var t1 := c compiled.
    
    t1().   
]

The output is:

Hello

Or we may use TapeAssembly class to create a class in run-time:

import system'dynamic.
import extensions'dynamic.
 
public program
[
    var a1 := ClosureTape::(
        openSingletonClosure,
        openMethodClosure,
        newMessageClosure new:"eval",
        newParamTokenClosure new:"n",
        newParamTokenClosure new:"self",
        openCodeClosure,        
        openExpressionClosure,
        newReferenceClosure new:"system'console",
        newMessageClosure new:"writeLine",
        newIdentifierClosure new:"n",
        closeClosure,        
        closeClosure,
        closeClosure,
        closeClosure).
  
    var o := TapeAssembly new(a1); eval.
    o eval("Hello again").
]

Output is :

Hello  again

We may use TapeExpression to simplify the code. Let's create a simple class with a single method which counts down the parameter and prints the values:

import extensions'dynamic'expressions.

public program
[
    // singleton Class
    var c := TapeExpression Singleton(
    // method eval(object m)
                TapeExpression Method(
                   "eval",
                   TapeExpression Code(
   // var m := n.
                      TapeExpression Declaring("m"),
                      TapeExpression Assigning(
                        "m",
                        TapeExpression Variable("n")
                      ),
   // while (m > 0)
                      TapeExpression Loop(
                          TapeExpression MessageCall(
                             TapeExpression Variable("m"), 
                             "greater",
                             TapeExpression Constant(0)),
                          TapeExpression Code(
   // console writeLine(m)
                              TapeExpression MessageCall(
                                 TapeExpression Constant(console), 
                                 "writeLine",
                                 TapeExpression Variable("m")),
   // m := m - 1
                              TapeExpression Assigning(
                                "m",
                                TapeExpression MessageCall(
                                    TapeExpression Variable("m"),
                                    "subtract",
                                    TapeExpression Constant(1)))))),
                      TapeExpression Parameter("n"))).

    var o := (c compiled)().   
    
    o eval(5).
]

The output is:

5
4
3
2
1

Let's implement the following code dynamically:

{ test : n = if(n == 2)[^ 2]. ^ 0 }

To implement the condition we will use TapeExpression.If(condition, truePart) method :

import extensions.
import extensions'dynamic'expressions.

public program
[
    var c := TapeExpression Singleton(
                TapeExpression Method(
                   "test",
                   TapeExpression Code(
                      TapeExpression If(
                        TapeExpression MessageCall(
                            TapeExpression Variable("n"),
                            "equal",
                            TapeExpression Constant(2)
                        ),
                        TapeExpression Code(
                            TapeExpression Returning(
                                TapeExpression Variable("n")
                            ))),
                      TapeExpression Returning(
                        TapeExpression Constant(0)
                      )),
                      TapeExpression Parameter("n"))).

    var t1 := c compiled.

    var o := t1().   

    console printLine("test(2)=",o test(2)).
    console printLine("test(3)=",o test(3)).
]

The output is :

test(2)=2
test(3)=0     

Now let's evaluate the code in run-time using ELENA Script Engine :

import extensions.
import extensions'scripting.

public program
[
    escript eval("system'console writeLine(""Hello World"").").
    
    var o := escript eval(
       "^ { eval(x) [ ^extensions'math'mathControl power(x, 2 ) ]}").

    console printLine(o eval(2)).
]

The output is :

ELENA VM 3.3.1 (C)2005-2017 by Alex Rakov
Initializing...
Debug mode...
Done...
Hello World
4

Now we will invoke our code from IDE.

Let's write a simple class :

import extensions.

public singleton MyClassToTest
{
    foo
    [
        console printLine("foo fired!").
    ]
}

public program 
[
]

Now let's compile the code : Project - Compile and open Interactive window : View - ELENA Interactive and type in the window (presuming test is the project root namespace):

>test'MyClassToTest foo.

The output is :

ELENA command line VM terminal 3.3.2 (C)2011-2018 by Alexei Rakov
ELENA VM 3.3.1 (C)2005-2017 by Alex Rakov
Initializing...
Debug mode...
Done...

>test'MyClassToTest foo.
foo fired!

More examples

You can get more examples and learn more about ELENA language in Rosseta Code:

http://rosettacode.org/wiki/Category:Elena

More articles about Elena programming language

Any question ?

Feel free to ask!

Who uses ELENA language and for what?

That is a question that has been on everyone's mind, when they see something about a new language .

Well, I use the language, and develop business programs in ELENA language

The programs that I developed in ELENA control all payments received by automakers banks such as Ford, GM, (Mercedes) Fundo Estrela, (Citroën and Peugeot) PSA Bank, (Fiat, Chrysler and used cars by Fiat) FIDIS Bank, this means that if you live in Brazil and bought a car from one of these manufacturers, their payment has been processed by a program written in ELENA.

And I wrote some prograns to Bradesco Bank and HSBC bank to process and generate payment data.

Open source projects in Elena

History

  • 6-Aug-18
    - Adding new topics : working with enumerable objects, mixins
  • 13-Jun-18
    - Updated to be compatible with ELENA 3.4
    - fixing spelling
  • 26-Jun-17
    - Added community group
  • 19-Jun-17
    - Added new version of compiler to download
  • 09-Jun-17
    - Updated all the article to the new version of Elena language
    - Added the new release of elena langauge to download
    - Added section "if-else template"
    - Added section "Open source projects in Elena"
  • 17-Jan-17
    - Added link to the new Elena article in Code Project
  • 12-Dec-16
    - Updated elena compiler to the last version
  • 22-Sep-16:
    - Added link to rosseta code
  • 12-Sep-16:
    - Article created

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0

Share

About the Authors

No Biography provided

Alex Rakov
Software Developer (Senior)
Russian Federation Russian Federation
No Biography provided

You may also be interested in...

Comments and Discussions

 
QuestionA bit busy for new languages Pin
Michael Breeden7-Aug-18 8:28
memberMichael Breeden7-Aug-18 8:28 
AnswerRe: A bit busy for new languages Pin
Alex Rakov7-Aug-18 10:23
memberAlex Rakov7-Aug-18 10:23 
QuestionSyntactical Mutt Pin
Richard Eng15-Jun-18 12:32
memberRichard Eng15-Jun-18 12:32 
AnswerRe: Syntactical Mutt Pin
Alexandre Bencz12-Jul-18 1:08
memberAlexandre Bencz12-Jul-18 1:08 
QuestionThere's another Elena language Pin
Richard Eng15-Jun-18 5:45
memberRichard Eng15-Jun-18 5:45 
AnswerRe: There's another Elena language Pin
Alexandre Bencz12-Jul-18 1:09
memberAlexandre Bencz12-Jul-18 1:09 
GeneralRe: There's another Elena language Pin
Richard Eng13-Jul-18 8:43
memberRichard Eng13-Jul-18 8:43 
QuestionElena is not the better Smalltalk; Pharo is Pin
Nathan James14-Jun-18 20:03
memberNathan James14-Jun-18 20:03 
AnswerRe: Elena is not the better Smalltalk; Pharo is Pin
Alex Rakov14-Jun-18 21:31
memberAlex Rakov14-Jun-18 21:31 
GeneralRe: Elena is not the better Smalltalk; Pharo is Pin
Nathan James15-Jun-18 5:29
memberNathan James15-Jun-18 5:29 
QuestionYet another programming language?? Pin
joreyesm14-Jun-18 17:14
memberjoreyesm14-Jun-18 17:14 
QuestionSo what are the advantages? Pin
Galatei14-Jun-18 11:26
memberGalatei14-Jun-18 11:26 
AnswerRe: So what are the advantages? Pin
Alex Rakov14-Jun-18 21:26
memberAlex Rakov14-Jun-18 21:26 
GeneralRe: So what are the advantages? Pin
Galatei14-Jun-18 21:55
memberGalatei14-Jun-18 21:55 
AnswerRe: So what are the advantages? Pin
Alex Rakov14-Jun-18 22:17
memberAlex Rakov14-Jun-18 22:17 
AnswerRe: So what are the advantages? Pin
netizenk7-Aug-18 6:30
professionalnetizenk7-Aug-18 6:30 
GeneralRe: So what are the advantages? Pin
Galatei7-Aug-18 6:56
memberGalatei7-Aug-18 6:56 
GeneralRe: So what are the advantages? Pin
Alex Rakov7-Aug-18 10:17
memberAlex Rakov7-Aug-18 10:17 
GeneralRe: So what are the advantages? Pin
Galatei7-Aug-18 10:25
memberGalatei7-Aug-18 10:25 
GeneralRe: So what are the advantages? Pin
Alex Rakov7-Aug-18 10:39
memberAlex Rakov7-Aug-18 10:39 
PraiseRe: So what are the advantages? Pin
Alex Rakov7-Aug-18 10:19
memberAlex Rakov7-Aug-18 10:19 
QuestionNAFL Pin
oldcoder7714-Jun-18 9:46
memberoldcoder7714-Jun-18 9:46 
Questionbut why?! Pin
Marius Bancila13-Jun-18 2:53
professionalMarius Bancila13-Jun-18 2:53 
AnswerRe: but why?! Pin
Alex Rakov13-Jun-18 3:23
memberAlex Rakov13-Jun-18 3:23 
AnswerRe: but why?! Pin
Alex Rakov14-Jun-18 0:23
memberAlex Rakov14-Jun-18 0:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web01 | 2.8.181214.1 | Last Updated 6 Aug 2018
Article Copyright 2016 by Alexandre Bencz, Alex Rakov
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid