Category:Elena: Difference between revisions

no edit summary
No edit summary
Line 14:
== The simplest program ==
 
To create a simple console program we have to declare the public **program** symbolclosure in the project root namespace:
 
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*.
 
The statement should be terminated by a dot (ELENA is inspired by Smalltalk and uses its syntax notations)semicolon.
 
public program()
[{
console .writeLine("Hello!");
]}
 
In our example the action is **writeLine** and the parameter list consists of a single literalstring 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:
 
public program()
[{
console .writeLine("Hello!"); .writeLine("How are you?").;
]}
 
The result will be:
Line 43:
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:
Line 55:
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 literalstring constant value.
 
The assigning value can be an expression itself:
 
public program()
[{
console .writeLine("Hello!"); .writeLine("How are you?").;
var s := console readLine.readLine()
]}
 
ELENA is a dynamic language and in normalmost casecases we may not specify the variable type:
 
public program()
[{
var s := "Hello".;
console .writeLine(s).;
 
s := 2. ;
console .writeLine(s).;
]}
 
The output will be:
Line 89:
2
 
But it is still possible to specify the variable expected type:
 
T<LiteralValue>String s := "Hello".;
console .writeLine(s).;
 
*where system'LiteralValueString is a class representing text as a sequence of UTF-8 characters.*
 
We may use a class alias to simplify the code:
 
literalstring s := "Hello".; // literalstring is a LiteralValueString class alias
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:
 
literalstring s := "Hello".;
s := T<literal>(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 ==
Line 130 ⟶ 140:
=== 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:
Line 158 ⟶ 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**):
 
import extensions.;
public rogram =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:
Line 177 ⟶ 187:
Real number - 2.3456
 
=== The stringString typeType ===
 
LiteralValue**String** is used to store the text encoded in UTF-8. LiteralValusString 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.Length - 1]).
].}
 
The output is:
Line 198 ⟶ 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.
 
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.Length - 1]).
].}
 
The output is:
Line 213 ⟶ 223:
An index is out of range
Call stack:
system'Exception#class.new$system'LiteralValue[1]:exceptions.l(125)
system'OutOfRangeException#class.new[01]:exceptions.l(149156)
system'LiteralValueOutOfRangeException#class.getAt$system'IntNumbernew[10]:memoryexceptions.l(1191156)
system'String.at[1]:memory.l(1243)
mytest'program.#invoke[0]:test.l(8)
system'BaseFunction$inlineC.evalstart[0]:controlwin32_app.l(17239)
system'#inline1BF.start[1]startUp:win32_app.l(3752)
system'startUp(5)
 
We may use another class representing UTF-16 text (WideLiteralValue**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.Length - 1]).;
].}
 
The output will be correct this time:
Line 240 ⟶ 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:
 
import system'routines.;
import extensions.;
public program =()
[{
var s := "Привет".;
console .printLine("The first character of ",s," is ", s enumerator.FirstMember); firstMember).
console .printLine("The last character of ",s," is ", s enumerator; lastMember.LastMember).
].}
 
The output will be correct for any UTF-8 text:
Line 256 ⟶ 266:
The last character of Привет is т
 
=== Array typetypes ===
 
It is possible to declare a dynamicgeneric (_system'Array_) or staticstrong-typed template-based (_system'Array#1_) array.
 
import extensions.;
public program =()
[{
var staticArraystrongTypedArray := (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:
 
staticstrong-typed array 1,2,3
dynamic-typed array 1,b,2.3
 
== Basic arithmetic operations ==
Line 283 ⟶ 294:
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:
Line 307 ⟶ 318:
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 ==
Line 313 ⟶ 372:
 
if(<Boolean expression>)
{
[ /* doSomething if TRUE*/ ];
[ /*doSomehting doSomething if ELSETRUE*/ ].
}
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.readLine();
if(s == "good")
{ [ console writeLine("Me too") ];
[ console .writeLine("WhatMe happends?too") ]
]. }
else
{
console.writeLine("What happends?")
}
}
 
Several conditions can be checked:
 
public program =()
[{
console .writeLine("Hello!"); .writeLine("How are you?").;
var s := console readLine.readLine();
if((s == "good") || (s == "fine"))
{ [ console writeLine("Me too") ];
[ console .writeLine("WhatMe happens?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.readLine();
s =>
"good" [ console{ console.writeLine("Me too") ];}
"fine" [ console{ console.writeLine("Glad to hear") ];}
"bad" [ console{ console.writeLine("What's wrong?") ];}
"so so" [{ console .writeLine("It happens") ];}
!: [ console { 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.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.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 ==
Line 391 ⟶ 489:
Let's create a simple class :
 
import extensions.;
class MyClass
{
// a field
literalstring myString.;
// aan implicit constructor
constructor new(literalstring 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 new("This is printed by my class.").;
myClass printString.printString()
].}
The output will be:
Line 423 ⟶ 527:
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.")
]}
printprintMe()
[{
console .printLine("I'm a Parent Class.")
]}
}
class MyChild :: MyParent
{
constructor new()
<= new;() // calling the parent constructor
[{
console .printLine("Child Constructor.")
]}
printprintMe()
[{
// calling the parent method
super print.printMe();
console .printLine("I'm a Child Class.")
]}
}
public program =()
[{
var myClass := MyChild new.new();
myClass print.printMe()
].}
 
The output is:
Line 480 ⟶ 584:
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.printPrivate()
]}
}
programpublic =program()
[{
// Note that if the constructor explicitly is not declared
// the system'Object one (without input parameters) is inherited
var myClass := MyClass new. MyClass();
myClass printPublic.printPublic();
myClass printPrivate.printPrivate()
].}
The output is:
 
Calling from public print - private print.
mytest'$private'MyClass : Method mytest#printPrivate[0] 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(213236)
system'#inline1BF$inline16.start[10]:win32_app.l(252313)
mytest'program.#invoke[0]:test.l(2426)
system'BaseFunction$inline16.evalstart[0]:controlwin32_app.l(17239)
system'#inline1BF.start[1]startUp:win32_app.l(3752)
system'startUp(5)
 
=== Properties ===
 
In normal case the class fields cannot be accessed outside the class. That's why we may declare a special methodmethods to access it:
 
import extensions;
 
import extensions.
class MyClass
{
int _x.;
get int x() = _x.; // get accessor
set x(int o) // set accessor
[{
_x := o.
]}
}
public program =()
[{
var myClass := MyClass new. MyClass();
myClass .x := 2.;
console .printLine("MyClass.x=", myClass .x).
].}
The output is:
Line 552 ⟶ 655:
MyClass.x=2
 
We may simplify our code if we will use **prop** attributefield template:
 
import extensions.;
class MyClass
{
int prop x :: _x.;
prop int x
{
get() = _x;
set(int val)
{
_x := val;
}
}
}
public program =()
[{
var myClass := 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 ==
Line 574 ⟶ 705:
We may use try-catch statement to handle the possible exceptions:
 
import extensions.;
import system'io.
public program =()
[{
try
try(console printLine(File new("notexistingfile.txt"); content))
{
on(Exceptionnew exObject().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 :
 
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 ==
Anonymous user