Category:Elena: Difference between revisions
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 |
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 |
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 |
The statement should be terminated by a semicolon. |
||
public program |
public program() |
||
{ |
|||
console |
console.writeLine("Hello!"); |
||
} |
|||
In our example the action is writeLine and the parameter list consists of a single |
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 |
Several message operations can be done in a single statement: |
||
public program |
public program() |
||
{ |
|||
console |
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 |
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 |
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 |
console.writeLine("Hello!").writeLine("How are you?"); |
||
var s := console |
var s := console.readLine() |
||
} |
|||
ELENA is a dynamic language and in |
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 |
console.writeLine(s); |
||
s := 2 |
s := 2; |
||
console |
console.writeLine(s); |
||
} |
|||
The output will be: |
The output will be: |
||
Line 89: | Line 89: | ||
2 |
2 |
||
But it is |
But it is possible to specify the variable expected type: |
||
String s := "Hello"; |
|||
console |
console.writeLine(s); |
||
where system' |
*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: |
||
string s := "Hello"; // string is a String class alias |
|||
console |
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: |
|||
string s := "Hello"; |
|||
s := |
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 |
console.printLine(b1,"==",b1," is ",b1==b1); |
||
console |
console.printLine(b2,"==",b2," is ",b2==b2); |
||
console |
console.printLine(b1,"==",b2," is ",b1==b2); |
||
console |
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 |
public program() |
||
{ |
|||
int n := -234 |
int n := -234; |
||
long l := 1235456765l |
long l := 1235456765l; |
||
real r := 2.3456r |
real r := 2.3456r; |
||
console |
console.printLine("Integer number - ",n); |
||
console |
console.printLine("Long integer number - ",l); |
||
console |
console.printLine("Real number - ",r) |
||
} |
|||
The output is: |
The output is: |
||
Line 177: | Line 187: | ||
Real number - 2.3456 |
Real number - 2.3456 |
||
=== The |
=== 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 |
import extensions; |
||
public program |
public program() |
||
{ |
|||
var s := "Hello" |
var s := "Hello"; |
||
console |
console.printLine("The first character of ",s," is ", s[0]); |
||
console |
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 |
console.printLine("The first character of ",s," is ", s[0]); |
||
console |
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'Exception#class.new[1]:exceptions.l(125) |
||
system'OutOfRangeException#class.new[ |
system'OutOfRangeException#class.new[1]:exceptions.l(156) |
||
system' |
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' |
system'$inlineC.start[0]:win32_app.l(39) |
||
system'# |
system'#startUp:win32_app.l(52) |
||
system'startUp(5) |
|||
We may use another class representing UTF-16 text ( |
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 |
console.printLine("The first character of ",s," is ", s[0]); |
||
console |
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 |
console.printLine("The first character of ",s," is ", s.FirstMember); |
||
console |
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 |
=== Array types === |
||
It is possible to declare a |
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 |
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: |
||
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 |
console.printLine(n1, " + ", n2, " = ", n1 + n2); |
||
console |
console.printLine(n1, " - ", n2, " = ", n1 - n2); |
||
console |
console.printLine(n1, " * ", n3, " = ", n1 * n3); |
||
console |
console.printLine(n1, " / ", n2, " = ", n1 / n2); |
||
console |
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*/ ]; |
|||
/* 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*/ }; |
|||
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 |
console.writeLine("Hello!").writeLine("How are you?"); |
||
var s := console |
var s := console.readLine(); |
||
if(s == "good") |
if(s == "good") |
||
{ |
|||
console.writeLine("Me too") |
|||
} |
|||
else |
|||
{ |
|||
console.writeLine("What happends?") |
|||
} |
|||
} |
|||
Several conditions can be checked: |
Several conditions can be checked: |
||
public program |
public program() |
||
{ |
|||
console |
console.writeLine("Hello!").writeLine("How are you?"); |
||
var s := console |
var s := console.readLine(); |
||
if((s == "good") || (s == "fine")) |
if((s == "good") || (s == "fine")) |
||
{ |
|||
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 |
console.writeLine("Hello!").writeLine("How are you?"); |
||
var s := console |
var s := console.readLine(); |
||
s => |
s => |
||
"good" |
"good" { console.writeLine("Me too") } |
||
"fine" |
"fine" { console.writeLine("Glad to hear") } |
||
"bad" |
"bad" { console.writeLine("What's wrong?") } |
||
"so so" |
"so so" { console.writeLine("It 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 |
console.writeLine("Hello!").writeLine("Guess what?"); |
||
var s := console |
var s := console.readLine(); |
||
while (s != "nothing") |
while (s != "nothing") |
||
{ |
|||
console |
console.writeLine("Guess what?"); |
||
s := console |
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 |
console.writeLine("Hello!").writeLine("Guess what?"); |
||
var s := console |
var s := console.readLine(); |
||
until (s == "nothing") |
until (s == "nothing") |
||
{ |
|||
console |
console.writeLine("Guess what?"); |
||
s := console |
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 |
||
string myString; |
|||
// |
// an implicit constructor |
||
constructor |
constructor(string s) |
||
{ |
|||
myString := s |
myString := s |
||
} |
|||
// an explicit constructor |
|||
constructor fromNuber(int n) |
|||
{ |
|||
myString := n.toString(); |
|||
} |
|||
// a method |
// a method |
||
printString |
printString() |
||
{ |
|||
console |
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 |
var myClass := new MyClass("This is printed by my class."); |
||
myClass |
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 |
console.printLine("Parent Constructor.") |
||
} |
|||
printMe() |
|||
{ |
|||
console |
console.printLine("I'm a Parent Class.") |
||
} |
|||
} |
} |
||
class MyChild |
class MyChild : MyParent |
||
{ |
{ |
||
constructor new |
constructor new() |
||
<= new |
<= new() // calling the parent constructor |
||
{ |
|||
console |
console.printLine("Child Constructor.") |
||
} |
|||
printMe() |
|||
{ |
|||
// calling the parent method |
// calling the parent method |
||
super |
super.printMe(); |
||
console |
console.printLine("I'm a Child Class.") |
||
} |
|||
} |
} |
||
public program |
public program() |
||
{ |
|||
var myClass := MyChild |
var myClass := MyChild.new(); |
||
myClass |
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 |
console.printLine("private print.") |
||
} |
|||
printPublic |
printPublic() |
||
{ |
|||
console |
console.print("Calling from public print - "); |
||
// |
// self is a reference to the current object |
||
self |
self.printPrivate() |
||
} |
|||
} |
} |
||
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 := |
var myClass := new MyClass(); |
||
myClass |
myClass.printPublic(); |
||
myClass |
myClass.printPrivate() |
||
} |
|||
The output is: |
The output is: |
||
Calling from public print - private print. |
Calling from public print - private print. |
||
mytest'MyClass : Method |
mytest'$private'MyClass : Method printPrivate[0] not found |
||
Call stack: |
Call stack: |
||
system'Exception#class.new |
system'Exception#class.new[1]:exceptions.l(125) |
||
system'MethodNotFoundException#class.new |
system'MethodNotFoundException#class.new[2]:exceptions.l(236) |
||
system' |
system'$inline16.start[0]:win32_app.l(313) |
||
mytest'program.#invoke[0]:test.l( |
mytest'program.#invoke[0]:test.l(26) |
||
system' |
system'$inline16.start[0]:win32_app.l(39) |
||
system'# |
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 |
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 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 := |
var myClass := new MyClass(); |
||
myClass |
myClass.x := 2; |
||
console |
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 |
We may simplify our code if we will use **prop** field template: |
||
import extensions |
import extensions; |
||
class MyClass |
class MyClass |
||
{ |
{ |
||
int |
int _x; |
||
prop int x |
|||
{ |
|||
get() = _x; |
|||
set(int val) |
|||
{ |
|||
_x := val; |
|||
} |
|||
} |
|||
} |
} |
||
public program |
public program() |
||
{ |
|||
var myClass := |
var myClass := new MyClass(); |
||
myClass |
myClass.x := 2; |
||
console |
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)) |
|||
{ |
{ |
||
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
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: |
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.
@
- Elena examples needing attention (empty)
- Elena Implementations (1 P)
- Elena User (2 P)
Pages in category "Elena"
The following 200 pages are in this category, out of 240 total.
(previous page) (next page)2
A
- A+B
- ABC problem
- Abstract type
- Abundant, deficient and perfect number classifications
- Accumulator factory
- Ackermann function
- Add a variable to a class instance at runtime
- AKS test for primes
- Amb
- Amicable pairs
- Anagrams
- Anonymous recursion
- Apply a callback to an array
- Arithmetic evaluation
- Arithmetic/Integer
- Array concatenation
- Array length
- Arrays
- Associative array/Creation
- Averages/Arithmetic mean
- Averages/Median
- Averages/Mode
- Averages/Root mean square
- Averages/Simple moving average
B
C
- Caesar cipher
- Calendar - for "REAL" programmers
- Call a function
- Call an object method
- Case-sensitivity of identifiers
- Catamorphism
- Character codes
- Check that file exists
- Classes
- Closures/Value capture
- Collections
- Combinations
- Command-line arguments
- Comments
- Compare a list of strings
- Compound data type
- Conway's Game of Life
- Copy a string
- Create a file
- Create a two-dimensional array at runtime
D
E
F
G
H
I
J
L
M
P
R
- Random number generator (included)
- Random numbers
- Read a file line by line
- Real constants and functions
- Reflection/List methods
- Reflection/List properties
- Remove duplicate elements
- Repeat a string
- Respond to an unknown method call
- Return multiple values
- Reverse a string
- Reverse words in a string
- Roman numerals/Decode
- Roman numerals/Encode
- Rot-13
- Run-length encoding
- Runtime evaluation
- Runtime evaluation/In an environment
S
- Search a list
- Search a list of records
- Send an unknown method call
- Sequence of primes by trial division
- Set of real numbers
- Short-circuit evaluation
- Simple windowed application
- Singleton
- Singly-linked list/Element definition
- Singly-linked list/Element insertion
- Singly-linked list/Traversal
- Sleep
- Sockets
- Sort an array of composite structures
- Sort an integer array
- Sort disjoint sublist
- Sort three variables
- Sort using a custom comparator
- Sorting algorithms/Bogosort
- Sorting algorithms/Bubble sort
- Sorting algorithms/Cocktail sort
- Sorting algorithms/Comb sort
- Sorting algorithms/Counting sort
- Sorting algorithms/Gnome sort
- Sorting algorithms/Insertion sort
- Sorting algorithms/Pancake sort
- Sorting algorithms/Quicksort
- Sorting algorithms/Selection sort
- Sorting algorithms/Sleep sort
- Sorting algorithms/Stooge sort
- Stack
- Stack traces
- String append
- String case
- String comparison
- String concatenation
- String interpolation (included)
- String length
- String matching
- String prepend
- Strip a set of characters from a string
- Strip whitespace from a string/Top and tail
- Substring
- Substring/Top and tail
- Sum and product of an array