Category:Elena: Difference between revisions

From Rosetta Code
Content added Content deleted
No edit summary
Line 14: Line 14:
== The simplest program ==
== The simplest program ==


To create a simple console program we have to declare the program symbol in the project root namespace:
To create a simple console program we have to declare the public **program** closure in the project root namespace:


public program
public program()
[
{
]
}


Everything in ELENA is an object. To interact with it we have to send a message. The simplest (generic, i.e. without an explicit signature) message consists of an action and a parameter list.
Everything in ELENA is an object. To interact with it we have to send a message. The message consists of *an action* and a *parameter list*.


The statement should be terminated by a dot (ELENA is inspired by Smalltalk and uses its syntax notations).
The statement should be terminated by a semicolon.


public program
public program()
[
{
console writeLine("Hello!")
console.writeLine("Hello!");
]
}


In our example the action is writeLine and the parameter list consists of a single literal constant. The message target is console object (implementing input / output operations with a program console).
In our example the action is **writeLine** and the parameter list consists of a single string constant. The message target is **console** object (implementing input / output operations with a program console).


Several message operations can be done in a single statement separated by a semicolon:
Several message operations can be done in a single statement:


public program
public program()
[
{
console writeLine("Hello!"); writeLine("How are you?").
console.writeLine("Hello!").writeLine("How are you?");
]
}


The result will be:
The result will be:
Line 43: Line 43:
How are you?
How are you?


We may read a user input by sending readLine message without parameters:
We may read a user input by sending **readLine** message without parameters:


public program
public program()
[
{
console write("What is your name:"); writeLine("Hello " + console readLine).
console.write("What is your name:").writeLine("Hello " + console.readLine())
]
}


The result will be:
The result will be:
Line 55: Line 55:
Hello Alex
Hello Alex


Console::write method is similar to writeLine except that it writes to the output screen without a new line character.
**Console::write** method is similar to **writeLine** except that it writes to the output screen without a new line character.


== Declaring a variable ==
== Declaring a variable ==


A variable can be declared in an assignment statement starting with var attribute:
A variable can be declared in an assignment statement starting with **var** attribute:


var myVariable := "A text".
var myVariable := "A text";


where we declare a variable myVariable and initialize it with a literal constant value.
where we declare a variable **myVariable** and initialize it with a string constant value.


The assigning value can be an expression itself:
The assigning value can be an expression itself:


public program
public program()
[
{
console writeLine("Hello!"); writeLine("How are you?").
console.writeLine("Hello!").writeLine("How are you?");
var s := console readLine.
var s := console.readLine()
]
}


ELENA is a dynamic language and in normal case we may not specify the variable type:
ELENA is a dynamic language and in most cases we may not specify the variable type:


public program
public program()
[
{
var s := "Hello".
var s := "Hello";
console writeLine(s).
console.writeLine(s);


s := 2.
s := 2;
console writeLine(s).
console.writeLine(s);
]
}


The output will be:
The output will be:
Line 89: Line 89:
2
2


But it is still possible to specify the variable expected type:
But it is possible to specify the variable expected type:


T<LiteralValue> s := "Hello".
String s := "Hello";
console writeLine(s).
console.writeLine(s);


where system'LiteralValue is a class representing text as a sequence of UTF-8 characters.
*where system'String is a class representing text as a sequence of UTF-8 characters.*


We may use a class alias to simplify the code:
We may use a class alias to simplify the code:


literal s := "Hello". // literal is a LiteralValue alias
string s := "Hello"; // string is a String class alias
console writeLine(s).
console.writeLine(s);


ELENA does not enforce types in compilation-time, so the following code will be successfully compiled:
Note that despite it we may still assign the objects of different types without a compile-time error:


literal s := "Hello".
string s := "Hello";
s := T<literal>(2).
s := 2;


But it will raise an exception in the run-time:
Why? ELENA is a dynamic language and in most cases resolves the types in run-time. So our code will be actually
compiled as follow:


system'IntNumber : Method #cast[0] not found
literal s := "Hello".
Call stack:
s := T<literal>(2)
system'Exception#class.new[1]:exceptions.l(125)
system'MethodNotFoundException#class.new[2]:exceptions.l(236)
system'$inlineC.start[0]:win32_app.l(313)
mytest'program.#invoke[0]:test.l(5)
system'$inlineC.start[0]:win32_app.l(39)
system'#startUp:win32_app.l(52)


As you may see, the compiler injects the typecasting code, so the actual code looks like this:
It is guaranteed that the result of the cast message is an instance of LiteralValue, so if the object supports the message the conversion will be done quietly.


string s := "Hello";
The output will be:
s := cast string(2);


*where* **cast string(2)** *is a construction to typecast the target expression* - **a numeric constant** - *to the expected type* - **System'String**
system'RealNumber : Method $system'IntNumber not found

Call stack:
As a result if the object supports casting method the operation will work. For example, system'IntNumber can be implicitly converted into system'RealNumber so the following code:
system'Exception#class.new$system'LiteralValue[1]:exceptions.l(125)

system'MethodNotFoundException#class.new$system'Object$system'Message[2]:exceptions.l(213)
public program()
system'#inline1BF.start[1]:win32_app.l(252)
{
mytest'program.#invoke[0]:test.l(5)
real r := 2;
system'BaseFunction.eval[0]:control.l(172)
console.writeLine(r)
system'#inline1BF.start[1]:win32_app.l(37)
}
system'startUp(5)
will be successfully executed with the result:

2.0


== Basic Types ==
== Basic Types ==
Line 130: Line 140:
=== The Boolean Type ===
=== The Boolean Type ===


Boolean type is used in conditional operations and may accept only two Boolean literals - true and false.
Boolean type is used in conditional operations and may accept only two Boolean literals - **true** and **false**.


import extensions.
import extensions;
public program
public program()
[
{
bool b1 := true.
bool b1 := true;
bool b2 := false.
bool b2 := false;
console printLine(b1,"==",b1," is ",b1==b1).
console.printLine(b1,"==",b1," is ",b1==b1);
console printLine(b2,"==",b2," is ",b2==b2).
console.printLine(b2,"==",b2," is ",b2==b2);
console printLine(b1,"==",b2," is ",b1==b2).
console.printLine(b1,"==",b2," is ",b1==b2);
console printLine(b2,"==",b1," is ",b1==b2).
console.printLine(b2,"==",b1," is ",b1==b2);
]
}


Note that implicit extension method - extensions'outputOp.printLine[] - was used to simplify the output operations.
*Note that implicit extension method - **extensions'outputOp.printLine[]** - was used to simplify the output operations.*


The output is:
The output is:
Line 158: Line 168:
The most used numeric types in ELENA are 32-bit signed integer number (represented by **IntNumber**), 64-bit signed integer number (represented by **LongNumber**) and 64-bit floating-point number (represented by **RealNumber**):
The most used numeric types in ELENA are 32-bit signed integer number (represented by **IntNumber**), 64-bit signed integer number (represented by **LongNumber**) and 64-bit floating-point number (represented by **RealNumber**):


import extensions.
import extensions;
public rogram =
public program()
[
{
int n := -234.
int n := -234;
long l := 1235456765l.
long l := 1235456765l;
real r := 2.3456r.
real r := 2.3456r;
console printLine("Integer number - ",n).
console.printLine("Integer number - ",n);
console printLine("Long integer number - ",l).
console.printLine("Long integer number - ",l);
console printLine("Real number - ",r).
console.printLine("Real number - ",r)
].
}


The output is:
The output is:
Line 177: Line 187:
Real number - 2.3456
Real number - 2.3456


=== The string type ===
=== The String Type ===


LiteralValue is used to store the text encoded in UTF-8. LiteralValus is read-only collection of CharValue classes each representing UTF-32 symbol. Note that one character may be encoded with more than one byte!
**String** is used to store the text encoded in UTF-8. String is a read-only collection of **CharValue** classes each representing UTF-32 symbol. *Note that one character may be encoded with more than one byte!*.


import extensions.
import extensions;
public program =
public program()
[
{
var s := "Hello".
var s := "Hello";
console printLine("The first character of ",s," is ", s[0]).
console.printLine("The first character of ",s," is ", s[0]);
console printLine("The last character of ",s," is ", s[s length - 1]).
console.printLine("The last character of ",s," is ", s[s.Length - 1])
].
}


The output is:
The output is:
Line 198: Line 208:
The same code for example with a Russian text will not work. Because every character is encoded with a two bytes and this should be taken into account.
The same code for example with a Russian text will not work. Because every character is encoded with a two bytes and this should be taken into account.


import extensions.
import extensions;


public program =
public program()
[
{
var s := "Привет".
var s := "Привет";
console printLine("The first character of ",s," is ", s[0]).
console.printLine("The first character of ",s," is ", s[0]);
console printLine("The last character of ",s," is ", s[s length - 1]).
console.printLine("The last character of ",s," is ", s[s.Length - 1])
].
}


The output is:
The output is:
Line 213: Line 223:
An index is out of range
An index is out of range
Call stack:
Call stack:
system'Exception#class.new$system'LiteralValue[1]:exceptions.l(125)
system'Exception#class.new[1]:exceptions.l(125)
system'OutOfRangeException#class.new[0]:exceptions.l(149)
system'OutOfRangeException#class.new[1]:exceptions.l(156)
system'LiteralValue.getAt$system'IntNumber[1]:memory.l(1191)
system'OutOfRangeException#class.new[0]:exceptions.l(156)
system'String.at[1]:memory.l(1243)
mytest'program.#invoke[0]:test.l(8)
mytest'program.#invoke[0]:test.l(8)
system'BaseFunction.eval[0]:control.l(172)
system'$inlineC.start[0]:win32_app.l(39)
system'#inline1BF.start[1]:win32_app.l(37)
system'#startUp:win32_app.l(52)
system'startUp(5)


We may use another class representing UTF-16 text (WideLiteralValue) to solve this problem:
We may use another class representing UTF-16 text (**WideString**) to solve this problem:


import extensions.
import extensions;
public program =
public program()
[
{
var s := "Привет"w. // UTF-16 string
var s := "Привет"w. // UTF-16 string
console printLine("The first character of ",s," is ", s[0]).
console.printLine("The first character of ",s," is ", s[0]);
console printLine("The last character of ",s," is ", s[s length - 1]).
console.printLine("The last character of ",s," is ", s[s.Length - 1]);
].
}


The output will be correct this time:
The output will be correct this time:
Line 240: Line 250:
But this code will not work with Chinese text or any other requiring more than 2 bytes per symbol. So instead we may use enumerators:
But this code will not work with Chinese text or any other requiring more than 2 bytes per symbol. So instead we may use enumerators:


import system'routines.
import system'routines;
import extensions.
import extensions;
public program =
public program()
[
{
var s := "Привет".
var s := "Привет";
console printLine("The first character of ",s," is ", s enumerator; firstMember).
console.printLine("The first character of ",s," is ", s.FirstMember);
console printLine("The last character of ",s," is ", s enumerator; lastMember).
console.printLine("The last character of ",s," is ", s.LastMember)
].
}


The output will be correct for any UTF-8 text:
The output will be correct for any UTF-8 text:
Line 256: Line 266:
The last character of Привет is т
The last character of Привет is т


=== Array type ===
=== Array types ===


It is possible to declare a dynamic or static array.
It is possible to declare a generic (_system'Array_) or strong-typed template-based (_system'Array#1_) array.


import extensions.
import extensions;
public program =
public program()
[
{
var staticArray := (1,2,3).
var strongTypedArray := new int[] {1,2,3};
var dynamicArray := Array new(3).
dynamicArray[0] := 1.
dynamicArray[1] := "b".
dynamicArray[2] := 2.3r.
var genericArray := Array.allocate(3);
console printLine("static array ",staticArray).
genericArray[0] := 1;
console printLine("dynamic array ",dynamicArray).
genericArray[1] := "b";
].
genericArray[2] := 2.3r;
console.printLine("strong-typed array ",strongTypedArray.asEnumerable());
console.printLine("dynamic-typed array ",genericArray.asEnumerable());
}


The output is:
The output is:


static array 1,2,3
strong-typed array 1,2,3
dynamic array 1,b,2.3
dynamic-typed array 1,b,2.3


== Basic arithmetic operations ==
== Basic arithmetic operations ==
Line 283: Line 294:
ELENA supports basic arithmetic operations with integer and floating-point numbers:
ELENA supports basic arithmetic operations with integer and floating-point numbers:


import extensions.
import extensions;

public program =
public program()
[
{
var n1 := 12.
var n1 := 12;
var n2 := 5.
var n2 := 5;
var n3 := -3.
var n3 := -3;
var r1 := 2.3r.
var r1 := 2.3r;
console printLine(n1, " + ", n2, " = ", n1 + n2).
console.printLine(n1, " + ", n2, " = ", n1 + n2);
console printLine(n1, " - ", n2, " = ", n1 - n2).
console.printLine(n1, " - ", n2, " = ", n1 - n2);
console printLine(n1, " * ", n3, " = ", n1 * n3).
console.printLine(n1, " * ", n3, " = ", n1 * n3);
console printLine(n1, " / ", n2, " = ", n1 / n2).
console.printLine(n1, " / ", n2, " = ", n1 / n2);


console printLine(n1, " + ", n2, " * ", r1 ," = ", n1 + n2 * r1).
console.printLine(n1, " + ", n2, " * ", r1 ," = ", n1 + n2 * r1)
].
}


The result is:
The result is:
Line 307: Line 318:
12 / 5 = 2
12 / 5 = 2
12 + 5 * 2.3 = 23.5
12 + 5 * 2.3 = 23.5

== ?? operator ==

Operator ?? is used to deal with nil.

_a ?? b_ - will return _a_ if _a_ is not _nil_ or _b_

See the following code:

import extensions;
public program()
{
var a := nil;
var b := a ?? 0 + 2;
console.printLine(a ?? "nil", " ?? 0 + 2 = ", b);
a := 1;
b := a ?? 0 + 2;
console.printLine(a ?? "nil", " ?? 0 + 2 = ", b);
}

The output is:

nil ?? 0 + 2 = 2
1 ?? 0 + 2 = 3

The operator can be used for typecasting operations as well:

_cast type(a) ?? b_ - will typecast _a_ to _type_ or return _b_ if it is not possible.

See the code:

import extensions;
public program()
{
string s := "a";
var n := 2;
console.printLine("cast int(",s,") ?? 0 = ", cast int(s) ?? 0);
console.printLine("cast int(",n,") ?? 0 = ", cast int(n) ?? 0);
}

The output is:

cast int(a) ?? 0 = 0
cast int(2) ?? 0 = 2


== Conditions, Multi-select, Loops ==
== Conditions, Multi-select, Loops ==
Line 313: Line 372:


if(<Boolean expression>)
if(<Boolean expression>)
{
[ /* doSomething if TRUE*/ ];
[ /*doSomehting if ELSE*/ ].
/* doSomething if TRUE*/
}
else
{
/*doSomehting if ELSE*/
};


We could omit else part
We could omit else part


if(<Boolean expression>)
if(<Boolean expression>)
[ /*doSomehting if TRUE*/ ].
{ /*doSomehting if TRUE*/ };


Usually Boolean expression is a result of a comparison operation:
Usually Boolean expression is a result of a comparison operation:


public program =
public program()
[
{
console writeLine("Hello!"); writeLine("How are you?").
console.writeLine("Hello!").writeLine("How are you?");
var s := console readLine.
var s := console.readLine();
if(s == "good")
if(s == "good")
[ console writeLine("Me too") ];
{
[ console writeLine("What happends?") ]
console.writeLine("Me too")
].
}
else
{
console.writeLine("What happends?")
}
}


Several conditions can be checked:
Several conditions can be checked:


public program =
public program()
[
{
console writeLine("Hello!"); writeLine("How are you?").
console.writeLine("Hello!").writeLine("How are you?");
var s := console readLine.
var s := console.readLine();
if((s == "good") || (s == "fine"))
if((s == "good") || (s == "fine"))
[ console writeLine("Me too") ];
{
[ console writeLine("What happens?") ]
console.writeLine("Me too")
].
}
else
{
console.writeLine("What happends?")
}
}


A switch statement can be implemented using => operator:
A switch statement can be implemented using => operator:


public program =
public program()
[
{
console writeLine("Hello!"); writeLine("How are you?").
console.writeLine("Hello!").writeLine("How are you?");
var s := console readLine.
var s := console.readLine();
s =>
s =>
"good" [ console writeLine("Me too") ];
"good" { console.writeLine("Me too") }
"fine" [ console writeLine("Glad to hear") ];
"fine" { console.writeLine("Glad to hear") }
"bad" [ console writeLine("What's wrong?") ];
"bad" { console.writeLine("What's wrong?") }
"so so" [ console writeLine("It happens") ];
"so so" { console.writeLine("It happens") }
! [ console writeLine("What happens?") ].
: { console.writeLine("What happens?") };
].
}


We could declare *while* loop which will be repeated until the condition is true:
We could declare *while* loop which will be repeated until the condition is true:


public program =
public program()
[
{
console writeLine("Hello!"); writeLine("Guess what?").
console.writeLine("Hello!").writeLine("Guess what?");
var s := console readLine.
var s := console.readLine();
while (s != "nothing")
while (s != "nothing")
[
{
console writeLine("Guess what?").
console.writeLine("Guess what?");
s := console readLine
s := console.readLine();
]
}
].
}


Alternatively *until* loop is executed until the condition is met :
Alternatively *until* loop is executed until the condition is met :


public program =
public program()
[
{
console writeLine("Hello!"); writeLine("Guess what?").
console.writeLine("Hello!").writeLine("Guess what?");
var s := console readLine.
var s := console.readLine();
until (s == "nothing")
until (s == "nothing")
[
{
console writeLine("Guess what?").
console.writeLine("Guess what?");
s := console readLine
s := console.readLine();
]
}
].
}

ELENA supports C-styled *for* loop as well:

public program()
{
for(int n := 0, n < 5, n += 1)
{
console.write("*")
}
}

The output is:

*****

_Note that comma is used instead of semicolon!_

*doUntil* loop is similar to *for*, but the loop is executed at least once:

var text := new StringWriter();
doUntil(string line, line.isEmpty(), line := console.readLine())
{
text.writeLine(line)
};


== Classes, Fields Methods, Constructors ==
== Classes, Fields Methods, Constructors ==
Line 391: Line 489:
Let's create a simple class :
Let's create a simple class :


import extensions.
import extensions;
class MyClass
class MyClass
{
{
// a field
// a field
literal myString.
string myString;
// a constructor
// an implicit constructor
constructor new(literal s)
constructor(string s)
[
{
myString := s.
myString := s
]
}
// an explicit constructor
constructor fromNuber(int n)
{
myString := n.toString();
}
// a method
// a method
printString
printString()
[
{
console printLine(myString).
console.printLine(myString)
]
}
}
}
public program =
public program()
[
{
// creating a class instance by sending new message to the class
// creating a class instance by sending new message to the class
var myClass := MyClass new("This is printed by my class.").
var myClass := new MyClass("This is printed by my class.");
myClass printString.
myClass.printString()
].
}
The output will be:
The output will be:
Line 423: Line 527:
This is printed by my class.
This is printed by my class.


Note that in ELENA a class is an object itself and can be used by like any other object.
*Note that in ELENA a class is an object itself and can be used by like any other object*


=== Class Inheritance ===
=== Class Inheritance ===


We may inherit our class. When the parent is not explicitly declared - the class inherits system'Object super class
We may inherit our class. When the parent is not explicitly declared - the class inherits *system'Object* super class


import extensions.
import extensions;
class MyParent
class MyParent
{
{
constructor new
constructor new()
[
{
console printLine("Parent Constructor.")
console.printLine("Parent Constructor.")
]
}
print
printMe()
[
{
console printLine("I'm a Parent Class.")
console.printLine("I'm a Parent Class.")
]
}
}
}
class MyChild :: MyParent
class MyChild : MyParent
{
{
constructor new
constructor new()
<= new; // calling the parent constructor
<= new() // calling the parent constructor
[
{
console printLine("Child Constructor.")
console.printLine("Child Constructor.")
]
}
print
printMe()
[
{
// calling the parent method
// calling the parent method
super print.
super.printMe();
console printLine("I'm a Child Class.")
console.printLine("I'm a Child Class.")
]
}
}
}
public program =
public program()
[
{
var myClass := MyChild new.
var myClass := MyChild.new();
myClass print.
myClass.printMe()
].
}


The output is:
The output is:
Line 480: Line 584:
It is possible to declare the private methods which cannot be called outside the class.
It is possible to declare the private methods which cannot be called outside the class.


import extensions.
import extensions;
class MyClass
class MyClass
{
{
private printPrivate
private printPrivate()
[
{
console printLine("private print.")
console.printLine("private print.")
]
}
printPublic
printPublic()
[
{
console print("Calling from public print - ").
console.print("Calling from public print - ");
// $self is a reference to the current object
// self is a reference to the current object
self printPrivate.
self.printPrivate()
]
}
}
}
program =
public program()
[
{
// Note that if the constructor explicitly is not declared
// Note that if the constructor explicitly is not declared
// the system'Object one (without input parameters) is inherited
// the system'Object one (without input parameters) is inherited
var myClass := MyClass new.
var myClass := new MyClass();
myClass printPublic.
myClass.printPublic();
myClass printPrivate.
myClass.printPrivate()
].
}
The output is:
The output is:


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


=== Properties ===
=== Properties ===


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

import extensions;


import extensions.
class MyClass
class MyClass
{
{
int _x.
int _x;
int x = _x. // get accessor
get int x() = _x; // get accessor
set x(int o) // set accessor
set x(int o) // set accessor
[
{
_x := o.
_x := o
]
}
}
}
public program =
public program()
[
{
var myClass := MyClass new.
var myClass := new MyClass();
myClass x := 2.
myClass.x := 2;
console printLine("MyClass.x=", myClass x).
console.printLine("MyClass.x=", myClass.x)
].
}
The output is:
The output is:
Line 552: Line 655:
MyClass.x=2
MyClass.x=2


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


import extensions.
import extensions;
class MyClass
class MyClass
{
{
int prop x :: _x.
int _x;
prop int x
{
get() = _x;
set(int val)
{
_x := val;
}
}
}
}
public program =
public program()
[
{
var myClass := MyClass new.
var myClass := new MyClass();
myClass x := 2.
myClass.x := 2;
console printLine("MyClass.x=", myClass x).
console.printLine("MyClass.x=", myClass.x)
].
}

Simple accessors can be omitted:

import extensions;
class MyClass
{
prop int x;
}
public program()
{
var myClass := new MyClass();
myClass.x := 2;
console.printLine("MyClass.x=", myClass.x)
}


== Exception Handling ==
== Exception Handling ==
Line 574: Line 705:
We may use try-catch statement to handle the possible exceptions:
We may use try-catch statement to handle the possible exceptions:


import extensions.
import extensions;
import system'io.
public program =
public program()
[
{
try
try(console printLine(File new("notexistingfile.txt"); content))
{
{
on(Exception ex)
new Object().nonExistingMethod();
[
console printLine("Unknown error - ",ex).
]
on(IOException ex)
[
console printLine("I/O error - ",ex).
]
}
}
catch(MethodNotFoundException e)
].
{
console.printLine("Method not found")
}
catch(Exception e)
{
console.printLine("Unknown error")
}
}
The output is :
The output is :


Method not found
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)


== See also ==
== See also ==

Revision as of 09:24, 6 January 2019

Language
Elena
This programming language may be used to instruct a computer to perform a task.
Official website
Execution method: Compiled (bytecode)
Garbage collected: Yes
Type safety: Safe
Type strength: Strong
Type expression: Implicit
Type checking: Dynamic
See Also:
Listed below are all of the tasks on Rosetta Code which have been solved using Elena.


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 mix-ins.

The simplest program

To create a simple console program we have to declare the public **program** closure in the project root namespace:

   public program()
   {
   }

Everything in ELENA is an object. To interact with it we have to send a message. The message consists of *an action* and a *parameter list*.

The statement should be terminated by a semicolon.

   public program()
   {
       console.writeLine("Hello!");
   }

In our example the action is **writeLine** and the parameter list consists of a single string constant. The message target is **console** object (implementing input / output operations with a program console).

Several message operations can be done in a single statement:

   public program()
   {
       console.writeLine("Hello!").writeLine("How are you?");
   }

The result will be:

    Hello!
    How are you?

We may read a user input by sending **readLine** message without parameters:

   public program()
   {
       console.write("What is your name:").writeLine("Hello " + console.readLine())
   }

The result will be:

   What is your name:Alex
   Hello Alex
    • Console::write** method is similar to **writeLine** except that it writes to the output screen without a new line character.

Declaring a variable

A variable can be declared in an assignment statement starting with **var** attribute:

   var myVariable := "A text";

where we declare a variable **myVariable** and initialize it with a string constant value.

The assigning value can be an expression itself:

   public program()
   {
       console.writeLine("Hello!").writeLine("How are you?");
       var s := console.readLine()
   }

ELENA is a dynamic language and in most cases we may not specify the variable type:

   public program()
   {
       var s := "Hello";
       console.writeLine(s);
       s := 2;
       console.writeLine(s);
   }

The output will be:

   Hello
   2

But it is possible to specify the variable expected type:

   String s := "Hello";
   console.writeLine(s);
  • where system'String is a class representing text as a sequence of UTF-8 characters.*

We may use a class alias to simplify the code:

   string s := "Hello";  // string is a String class alias
   console.writeLine(s);

ELENA does not enforce types in compilation-time, so the following code will be successfully compiled:

   string s := "Hello";
   s := 2;

But it will raise an exception in the run-time:

   system'IntNumber : Method #cast[0] not found
   Call stack:
   system'Exception#class.new[1]:exceptions.l(125)
   system'MethodNotFoundException#class.new[2]:exceptions.l(236)
   system'$inlineC.start[0]:win32_app.l(313)
   mytest'program.#invoke[0]:test.l(5)
   system'$inlineC.start[0]:win32_app.l(39)
   system'#startUp:win32_app.l(52)

As you may see, the compiler injects the typecasting code, so the actual code looks like this:

   string s := "Hello";
   s := cast string(2);
  • where* **cast string(2)** *is a construction to typecast the target expression* - **a numeric constant** - *to the expected type* - **System'String**

As a result if the object supports casting method the operation will work. For example, system'IntNumber can be implicitly converted into system'RealNumber so the following code:

   public program()
   {
       real r := 2;
       console.writeLine(r)
   }
  

will be successfully executed with the result:

   2.0

Basic Types

The Boolean Type

Boolean type is used in conditional operations and may accept only two Boolean literals - **true** and **false**.

   import extensions;
   
   public program()
   {
       bool b1 := true;
       bool b2 := false;
       
       console.printLine(b1,"==",b1," is ",b1==b1);
       console.printLine(b2,"==",b2," is ",b2==b2);
       console.printLine(b1,"==",b2," is ",b1==b2);
       console.printLine(b2,"==",b1," is ",b1==b2);
   }
  • Note that implicit extension method - **extensions'outputOp.printLine[]** - was used to simplify the output operations.*

The output is:

   true==true is true
   false==false is true
   true==false is false
   false==true is false

The Numeric types

The most used numeric types in ELENA are 32-bit signed integer number (represented by **IntNumber**), 64-bit signed integer number (represented by **LongNumber**) and 64-bit floating-point number (represented by **RealNumber**):

   import extensions;
   
   public program()
   {
       int  n := -234;
       long l := 1235456765l;
       real r := 2.3456r;
       
       console.printLine("Integer number - ",n);
       console.printLine("Long integer number - ",l);
       console.printLine("Real number - ",r)
   }

The output is:

   Integer number - -234
   Long integer number - 1235456765
   Real number - 2.3456

The String Type

    • String** is used to store the text encoded in UTF-8. String is a read-only collection of **CharValue** classes each representing UTF-32 symbol. *Note that one character may be encoded with more than one byte!*.
   import extensions;
   
   public program()
   {
       var s := "Hello";
       
       console.printLine("The first character of ",s," is ", s[0]);
       console.printLine("The last character of ",s," is ", s[s.Length - 1])
   }

The output is:

   The first character of Hello is H
   The last character of Hello is o

The same code for example with a Russian text will not work. Because every character is encoded with a two bytes and this should be taken into account.

   import extensions;
   public program()
   {
       var s := "Привет";
       
       console.printLine("The first character of ",s," is ", s[0]);
       console.printLine("The last character of ",s," is ", s[s.Length - 1])
   }

The output is:

   The first character of Привет is П
   An index is out of range
   Call stack:
   system'Exception#class.new[1]:exceptions.l(125)
   system'OutOfRangeException#class.new[1]:exceptions.l(156)
   system'OutOfRangeException#class.new[0]:exceptions.l(156)
   system'String.at[1]:memory.l(1243)
   mytest'program.#invoke[0]:test.l(8)
   system'$inlineC.start[0]:win32_app.l(39)
   system'#startUp:win32_app.l(52)

We may use another class representing UTF-16 text (**WideString**) to solve this problem:

   import extensions;
   
   public program()
   {
       var s := "Привет"w. // UTF-16 string
       
       console.printLine("The first character of ",s," is ", s[0]);
       console.printLine("The last character of ",s," is ", s[s.Length - 1]);
   }

The output will be correct this time:

   The first character of Привет is П
   The last character of Привет is т

But this code will not work with Chinese text or any other requiring more than 2 bytes per symbol. So instead we may use enumerators:

   import system'routines;
   import extensions;
   
   public program()
   {
       var s := "Привет";
       
       console.printLine("The first character of ",s," is ", s.FirstMember);
       console.printLine("The last character of ",s," is ", s.LastMember)
   }

The output will be correct for any UTF-8 text:

   The first character of Привет is П
   The last character of Привет is т

Array types

It is possible to declare a generic (_system'Array_) or strong-typed template-based (_system'Array#1_) array.

   import extensions;
   
   public program()
   {
       var strongTypedArray := new int[] {1,2,3};
       
       var genericArray := Array.allocate(3);
       genericArray[0] := 1;
       genericArray[1] := "b";
       genericArray[2] := 2.3r;
   
       console.printLine("strong-typed array ",strongTypedArray.asEnumerable());
       console.printLine("dynamic-typed array ",genericArray.asEnumerable());
   }

The output is:

   strong-typed array 1,2,3
   dynamic-typed array 1,b,2.3

Basic arithmetic operations

ELENA supports basic arithmetic operations with integer and floating-point numbers:

   import extensions;
   
   public program()
   {
       var n1 := 12;
       var n2 := 5;
       var n3 := -3;
       var r1 := 2.3r;
    
       console.printLine(n1, " + ", n2, " = ", n1 + n2);
       console.printLine(n1, " - ", n2, " = ", n1 - n2);
       console.printLine(n1, " * ", n3, " = ", n1 * n3);
       console.printLine(n1, " / ", n2, " = ", n1 / n2);
       console.printLine(n1, " + ", n2, " * ", r1 ," = ", n1 + n2 * r1)
   }

The result is:

   12 + 5 = 17
   12 - 5 = 7
   12 * -3 = -36
   12 / 5 = 2
   12 + 5 * 2.3 = 23.5

?? operator

Operator ?? is used to deal with nil.

_a ?? b_ - will return _a_ if _a_ is not _nil_ or _b_

See the following code:

   import extensions;
   
   public program()
   {
       var a := nil;
       var b := a ?? 0 + 2;
       console.printLine(a ?? "nil", " ?? 0 + 2 = ", b);
               
       a := 1;
       b := a ?? 0 + 2;
       console.printLine(a ?? "nil", " ?? 0 + 2 = ", b);
   }

The output is:

   nil ?? 0 + 2 = 2
   1 ?? 0 + 2 = 3

The operator can be used for typecasting operations as well:

_cast type(a) ?? b_ - will typecast _a_ to _type_ or return _b_ if it is not possible.

See the code:

   import extensions;
   
   public program()
   {
       string s := "a";
       var n := 2;
       
       console.printLine("cast int(",s,") ?? 0 = ", cast int(s) ?? 0);
       console.printLine("cast int(",n,") ?? 0 = ", cast int(n) ?? 0);
   }

The output is:

   cast int(a) ?? 0 = 0
   cast int(2) ?? 0 = 2

Conditions, Multi-select, Loops

Conditional statement in ELENA are defined as follows:

   if(<Boolean expression>)
   { 
      /* doSomething if TRUE*/ 
   }
   else
   {
      /*doSomehting if ELSE*/
   };

We could omit else part

   if(<Boolean expression>)
      { /*doSomehting if TRUE*/ };

Usually Boolean expression is a result of a comparison operation:

   public program()
   {
       console.writeLine("Hello!").writeLine("How are you?");
       var s := console.readLine();
       if(s == "good")
       { 
           console.writeLine("Me too") 
       }
       else
       {
           console.writeLine("What happends?")
       }
   }

Several conditions can be checked:

   public program()
   {
       console.writeLine("Hello!").writeLine("How are you?");
       var s := console.readLine();
       if((s == "good") || (s == "fine"))
       { 
           console.writeLine("Me too") 
       }
       else
       {
           console.writeLine("What happends?")
       }
   }

A switch statement can be implemented using => operator:

   public program()
   {
       console.writeLine("Hello!").writeLine("How are you?");
       var s := console.readLine();
       s =>
         "good"  { console.writeLine("Me too") }
         "fine"  { console.writeLine("Glad to hear") }
         "bad"   { console.writeLine("What's wrong?") }
         "so so" { console.writeLine("It happens") }
         :       { console.writeLine("What happens?") };
   }

We could declare *while* loop which will be repeated until the condition is true:

   public program()
   {
       console.writeLine("Hello!").writeLine("Guess what?");
       var s := console.readLine();
       while (s != "nothing")
       {
           console.writeLine("Guess what?");
           s := console.readLine();
       }
   }

Alternatively *until* loop is executed until the condition is met :

   public program()
   {
       console.writeLine("Hello!").writeLine("Guess what?");
       var s := console.readLine();
       until (s == "nothing")
       {
           console.writeLine("Guess what?");
           s := console.readLine();
       }
   }

ELENA supports C-styled *for* loop as well:

   public program()
   {
       for(int n := 0, n < 5, n += 1)
       {
           console.write("*")
       }            
   }

The output is:

   *****

_Note that comma is used instead of semicolon!_

  • doUntil* loop is similar to *for*, but the loop is executed at least once:
   var text := new StringWriter();
   doUntil(string line, line.isEmpty(), line := console.readLine())
   {
       text.writeLine(line)
   };

Classes, Fields Methods, Constructors

Everything in ELENA is a class. So to implement some tasks we will have to declare our own classes.

Declaring a simple class

Let's create a simple class :

   import extensions;
   
   class MyClass
   {
       // a field
       string myString;
   
       // an implicit constructor
       constructor(string s)
       {
           myString := s
       }
       
       // an explicit constructor
       constructor fromNuber(int n)
       {
           myString := n.toString();
       }
       
       // a method
       printString()
       {
           console.printLine(myString)
       }
   }
   
   public program()
   {
       // creating a class instance by sending new message to the class
       var myClass := new MyClass("This is printed by my class.");
       
       myClass.printString()
   }

The output will be:

   This is printed by my class.
  • Note that in ELENA a class is an object itself and can be used by like any other object*

Class Inheritance

We may inherit our class. When the parent is not explicitly declared - the class inherits *system'Object* super class

   import extensions;
   
   class MyParent
   {
       constructor new()
       {
           console.printLine("Parent Constructor.")
       }
   
       printMe()
       {
           console.printLine("I'm a Parent Class.")
       }    
   }
   
   class MyChild : MyParent
   {
       
       constructor new()
           <= new() // calling the parent constructor
       {
           console.printLine("Child Constructor.")
       }
       
       printMe()
       {
           // calling the parent method
           super.printMe();
           
           console.printLine("I'm a Child Class.")
       }
   }
   
   public program()
   {
       var myClass := MyChild.new();
       
       myClass.printMe()
   }

The output is:

   Parent Constructor.
   Child Constructor.
   I'm a Parent Class.
   I'm a Child Class.

Private methods

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 := new MyClass();
       
       myClass.printPublic();
       myClass.printPrivate()
   }

The output is:
   Calling from public print - private print.
   mytest'$private'MyClass : Method printPrivate[0] not found
   Call stack:
   system'Exception#class.new[1]:exceptions.l(125)
   system'MethodNotFoundException#class.new[2]:exceptions.l(236)
   system'$inline16.start[0]:win32_app.l(313)
   mytest'program.#invoke[0]:test.l(26)
   system'$inline16.start[0]:win32_app.l(39)
   system'#startUp:win32_app.l(52)

Properties

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

   import extensions;
   class MyClass
   {
       int _x;
   
       get int x() = _x;  // get accessor
   
       set x(int o)       // set accessor 
       {
          _x := o
       }
   }
   
   public program()
   {
       var myClass := new MyClass();
   
       myClass.x := 2;
   
       console.printLine("MyClass.x=", myClass.x)
   }

The output is:

   MyClass.x=2

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

   import extensions;
   
   class MyClass
   {
       int _x;
       
       prop int x
       {
           get() = _x;
           
           set(int val)
           {
               _x := val;
           }
       }
   }
   
   public program()
   {
       var myClass := new MyClass();
   
       myClass.x := 2;
   
       console.printLine("MyClass.x=", myClass.x)
   } 

Simple accessors can be omitted:

   import extensions;
   
   class MyClass
   {
       prop int x;
   }
   
   public program()
   {
       var myClass := new MyClass();
   
       myClass.x := 2;
   
       console.printLine("MyClass.x=", myClass.x)
   }

Exception Handling

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

   import extensions;
   
   public program()
   {
       try
       {
           new Object().nonExistingMethod();
       }
       catch(MethodNotFoundException e)
       {
           console.printLine("Method not found")
       }
       catch(Exception e)
       {
           console.printLine("Unknown error")
       }
   }

The output is :

   Method not found

See also

Subcategories

This category has the following 3 subcategories, out of 3 total.

Pages in category "Elena"

The following 200 pages are in this category, out of 240 total.

(previous page) (next page)
(previous page) (next page)