Category:Elena: Difference between revisions
No edit summary |
No edit summary |
||
Line 12: | Line 12: | ||
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. |
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 |
|||
== Namespaces == |
|||
To create a simple console program we have to declare the **program** symbol in the project root namespace: |
|||
Any ELENA program or library consists of modules ( files with .NL extension ) containing classes and symbols. Every member of the module is referred by its fully qualified name which consists of namespace and a proper name separated by an apostrophe. The namespace itself may contain sub elements separated by apostrophes. |
|||
program = |
|||
All source files (files with .L extension) located in the same folder are compiled into the corresponding module. A project file ( a file with .PRJ extension ) defines the root namespace and the output type (stand-alone executable, VM executable or a library). The project may produce several modules if it contains the files located in sub folders (the new module namespace consists of the root one and the folder relative path is split by apostrophes). |
|||
[ |
|||
]. |
|||
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*. |
|||
== Messaging == |
|||
The |
The statement should be terminated by a dot (*ELENA is inspired by Smalltalk and uses its syntax notations*). |
||
name is structured and consists of a operation, parameter signature and a parameter counter. |
|||
program = |
|||
operation [parameter_attribute]* [parameter_counter] |
|||
[ |
|||
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). |
|||
e.g. |
|||
Several message operations can be done in a single statement separated by a semicolon: |
|||
writeLine[2] - generic one |
|||
writeLine&LiteralValue&LiteralValue[2] - strong one |
|||
program = |
|||
If the signature is omitted the message may be multi-dispatched. |
|||
[ |
|||
console writeLine("Hello!"); writeLine("How are you?"). |
|||
]. |
|||
The result will be: |
|||
If the object wants to handle the message it has to contain the method with the same name. If no method mapping was found the flow is considered to be broken and the control goes to the next alternative flow (exception handler) or the program is stopped. |
|||
Hello! |
|||
The simple code to send a message looks like this: |
|||
How are you? |
|||
We may read a user input by sending **readLine** message without parameters: |
|||
console write:"Hello World". |
|||
program = |
|||
Note: *write* is a generic message; a literal constant is a parameter. A dot is a statement terminator. |
|||
[ |
|||
console write("What is your name:"); writeLine("Hello " + console readLine). |
|||
]. |
|||
The result will be: |
|||
If the parameter is an expression itself it should be enclose in the round brackets: |
|||
What is your name:Alex |
|||
console write:("Hello " add:"World"). |
|||
Hello Alex |
|||
**Console::write** method is similar to **writeLine** except that it writes to the output screen without a new line character. |
|||
In this case a colon may be dropped: |
|||
## Declaring a variable |
|||
console write("Hello " add:"World"). |
|||
A variable can be declared in an assignment statement starting with **var** attribute: |
|||
var myVariable := "A text". |
|||
console write:"2 + 2 ="; write(2 add:2). |
|||
where we declare a variable **myVariable** and initialize it with a literal constant value. |
|||
We could use operators to have the shorter code: |
|||
The assigning value can be an expression itself: |
|||
console write(2 + 2). |
|||
program = |
|||
Note: In most cases *"+"* is a synonym to *add*. |
|||
[ |
|||
console writeLine("Hello!"); writeLine("How are you?"). |
|||
var s := console readLine. |
|||
]. |
|||
ELENA is a dynamic language and in normal case we may not specify the variable type: |
|||
Several qualified parameters can be passed in the message: |
|||
program = |
|||
console write:"Hello World" paddingLeft:10 with:$32 |
|||
[ |
|||
var s := "Hello". |
|||
console writeLine(s). |
|||
s := 2. |
|||
The message name is *write&paddingLeft&with[2]*. |
|||
console writeLine(s). |
|||
]. |
|||
The output will be: |
|||
The generic message can have several parameters as well: |
|||
Hello |
|||
console writeLine("a+b=",a + b). |
|||
2 |
|||
But it is still possible to specify the variable expected type: |
|||
== Classes, Symbols, Nested classes and Closures == |
|||
type<LiteralValue> s := "Hello". |
|||
ELENA is an object-oriented language. To create a program we have to declare new classes and symbols. |
|||
console writeLine(s). |
|||
*where system'LiteralValue is a class representing text as a sequence of UTF-8 characters.* |
|||
A class encapsulates data (fields) with code (methods) to access it. In most cases it is not possible |
|||
to get a direct access to the class content. Usually the field refers to another class and so on |
|||
until we reach "primitive" ones which content are considered as raw data (e.g. numeric or literal values). |
|||
We may use a class alias to simplify the code: |
|||
Classes form the inheritance tree. There is the common super class - system'Object. |
|||
ELENA does not support multiple inheritance, though it is possible to inherit the code using a dispatch handler |
|||
(mixins / group objects). When the parent is not provided the class inherits directly system'Object |
|||
(the super class). |
|||
literal s := "Hello". // literal is a LiteralValue alias |
|||
A class instance can be created with the help of the special methods - constructors. A constructor |
|||
console writeLine(s). |
|||
is used mostly to initialize the class fields. There are special types of classes which do not have constructors |
|||
and can be used directly (singletons, nested classes, extensions, closures). A class itself is considered as a stateless object. |
|||
Note that despite it we may still assign the objects of different types without a compile-time error: |
|||
class BaseClass |
|||
{ |
|||
object theField1. |
|||
object theField2. |
|||
field1 = theField1. |
|||
field2 = theField2. |
|||
} |
|||
class DerivedClass :: BaseClass |
|||
{ |
|||
constructor new field1:object1 field2:object2 |
|||
[ |
|||
theField1 := object1. |
|||
theField2 := object2. |
|||
] |
|||
add field1:object1 field2:object2 |
|||
= MyClass new field1(theField1 + object1) field2(theField2 + object1). |
|||
} |
|||
To create a class instance we have to send a message (usually new) to its class. |
|||
literal s := "Hello". |
|||
var anObject := DerivedClass new field1:1 field2:1. |
|||
s := 2. |
|||
Why? ELENA is a dynamic language and in most cases resolves the types in run-time. So our code will be actually |
|||
A symbol is a named expression and can be used to declare initialized objects, constants, reusable expressions |
|||
compiled as follow: |
|||
and so on. |
|||
literal s := "Hello". |
|||
s := 2 literal. |
|||
symbol TheClass = DerivedClass new field1:N field2:N. |
|||
It is guaranteed that the result of **literal** message is an instance of LiteralValue, so if the object supports the message the conversion will be done quietly. In fact this code will work because **IntNumber** supports this conversion. But the following code will be broken in run-time: |
|||
A static symbol is the named expression which state is preserved. There could be only one instance of static symbol. |
|||
int n := 3. |
|||
static SingletonClass = DerivedClass new field1:0 field2:0. |
|||
n := 3.4r. |
|||
The output will be: |
|||
Nested classes can be declared in the code and used directly. |
|||
system'RealNumber : Method $system'IntNumber not found |
|||
var ClassHelper = |
|||
Call stack: |
|||
system'Exception#class.new$system'LiteralValue[1]:exceptions.l(125) |
|||
sumOf:object1:object2 |
|||
system'MethodNotFoundException#class.new$system'Object$system'Message[2]:exceptions.l(213) |
|||
= anObject1 add field1:object1 field2:object2. |
|||
system'#inline1BF.start[1]:win32_app.l(252) |
|||
}. |
|||
mytest'program.#invoke[0]:test.l(5) |
|||
system'BaseFunction.eval[0]:control.l(172) |
|||
system'#inline1BF.start[1]:win32_app.l(37) |
|||
system'startUp(5) |
|||
## Basic Types |
|||
### The Boolean Type |
|||
Boolean type is used in conditional operations and may accept only two Boolean literals - **true** and **false**. |
|||
import extensions. |
|||
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. |
|||
program = |
|||
var aSum := ClassHelper sumOf:anObject1:anObject2. |
|||
[ |
|||
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: |
|||
Closure is a special case of the nested class and consists of the code enclosed into square brackets |
|||
(with optional parameter declaration) |
|||
Integer number - -234 |
|||
str forEach:(:ch) [ console write:ch. ]. |
|||
Long integer number - 1235456765 |
|||
Real number - 2.3456 |
|||
### The string Type |
|||
Note: a colon may be dropped: |
|||
**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!*. |
|||
str forEach(:ch) [ console write:ch. ]. |
|||
import extensions. |
|||
== Code blocks == |
|||
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: |
|||
ELENA code block consists of one or more declarations and statements. The block is enclosed in square brackets and may contain nested sub code blocks. The statement terminator is a dot. |
|||
The first character of Hello is H |
|||
control run int:0 int:MAX every(:i) |
|||
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. |
|||
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: |
|||
console writeLine:pi. |
|||
console writeLine("Time elapsed in msec:",aDiff milliseconds). |
|||
The first character of Привет is П |
|||
When a method should return a result (other than $self) return statement is used. It should be the last statement in the block. |
|||
An index is out of range |
|||
Call stack: |
|||
system'Exception#class.new$system'LiteralValue[1]:exceptions.l(125) |
|||
system'OutOfRangeException#class.new[0]:exceptions.l(149) |
|||
system'LiteralValue.getAt$system'IntNumber[1]:memory.l(1191) |
|||
mytest'program.#invoke[0]:test.l(8) |
|||
system'BaseFunction.eval[0]:control.l(172) |
|||
system'#inline1BF.start[1]:win32_app.l(37) |
|||
system'startUp(5) |
|||
We may use another class representing UTF-16 text (**WideLiteralValue**) to solve this problem: |
|||
import extensions. |
|||
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. |
|||
program = |
|||
^ aRetVal / anArray length |
|||
[ |
|||
var s := "Привет". |
|||
console printLine("The first character of ",s," is ", s enumerator; firstMember). |
|||
console printLine("The last character of ",s," is ", s enumerator; lastMember). |
|||
]. |
|||
The output will be correct for any UTF-8 text: |
|||
If the code block contains only return statement the simplified syntax can be used: |
|||
The first character of Привет is П |
|||
Number = convertor toReal:theValue. |
|||
The last character of Привет is т |
|||
### Array type |
|||
It is possible to declare the block variable and assigns the value to it. The variable name must be unique within the code block scope. |
|||
It is possible to declare a dynamic or static array. |
|||
var aRetVal := Integer new:0. |
|||
import extensions. |
|||
program = |
|||
[ |
|||
var staticArray := (1,2,3). |
|||
var dynamicArray := Array new(3). |
|||
dynamicArray[0] := 1. |
|||
dynamicArray[1] := "b". |
|||
dynamicArray[2] := 2.3r. |
|||
console printLine("static array ",staticArray). |
|||
console printLine("dynamic array ",dynamicArray). |
|||
]. |
|||
The output is: |
|||
== Conditional branching == |
|||
static array 1,2,3 |
|||
Conditional branching is implemented with a help of special Boolean symbols (system’true and system’false). All conditional operations should return these symbols as a result. |
|||
dynamic array 1,b,2.3 |
|||
## Basic arithmetic operations |
|||
There are three branching methods : _if[1]_ , _if[2]_, _ifnot[1]_ |
|||
ELENA supports basic arithmetic operations with integer and floating-point numbers: |
|||
(m == 0) if: |
|||
import extensions. |
|||
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: |
|||
This expression can be written using special operators |
|||
12 + 5 = 17 |
|||
12 - 5 = 7 |
|||
12 * -3 = -36 |
|||
12 / 5 = 2 |
|||
12 + 5 * 2.3 = 23.5 |
|||
## Conditions, Multi-select, Loops |
|||
We could omit true or else part |
|||
Conditional statement in ELENA are defined as follows: |
|||
(m == 0) |
|||
! [ m / n ]. |
|||
if(<Boolean expression>) |
|||
Boolean symbols supports basic logical operations (AND, OR, XOR and NOT), so several conditions can be checked |
|||
[ /* doSomething if TRUE*/ ]; |
|||
[ /*doSomehting if ELSE*/ ]. |
|||
We could omit else part |
|||
((aChar >= 48) and:(aChar < 58)) |
|||
? [ |
|||
theToken += aChar |
|||
] |
|||
! [ |
|||
Exception new:"Invalid expression"; raise |
|||
] |
|||
if(<Boolean expression>) |
|||
**if** code template could be used: |
|||
[ /*doSomehting if TRUE*/ ]. |
|||
Usually Boolean expression is a result of a comparison operation: |
|||
if ((aChar >= 48) and:(aChar < 58)) |
|||
[ |
|||
theToken append:aChar |
|||
]; |
|||
[ |
|||
Exception new:"Invalid expression"; raise |
|||
]. |
|||
program = |
|||
Note that in this case both condition will be evaluated even if the first one is false. If we want to use short-circuit evaluation, lazy expression should be used: |
|||
[ |
|||
console writeLine("Hello!"); writeLine("How are you?"). |
|||
var s := console readLine. |
|||
if(s == "good") |
|||
[ console writeLine("Me too") ]; |
|||
[ console writeLine("What happends?") ] |
|||
]. |
|||
Several conditions can be checked: |
|||
if ((x >= 0)and:$(array@x != 0)) |
|||
program = |
|||
[ |
[ |
||
console writeLine("Hello!"); writeLine("How are you?"). |
|||
... |
|||
var s := console readLine. |
|||
] |
|||
if((s == "good") || (s == "fine")) |
|||
[ console writeLine("Me too") ]; |
|||
[ console writeLine("What happens?") ] |
|||
]. |
|||
A switch statement can be implemented using => operator |
A switch statement can be implemented using => operator: |
||
program = |
|||
[ |
|||
-1 [ console writeLine:"Not a valid guess.". ^ true ]; |
|||
console writeLine("Hello!"); writeLine("How are you?"). |
|||
4 [ |
|||
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?") ]. |
|||
]. |
|||
"Your Score is ", aBulls, |
|||
" bulls and ", aCows, " cows"). |
|||
^ true |
|||
]. |
|||
We could declare *while* loop which will be repeated until the condition is true: |
|||
== Hello world program == |
|||
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 : |
|||
To write a simple console application, we have to declare the program main symbol - an object handling _eval[0]_ message. The simplest way is to declare a nested class: |
|||
program = |
program = |
||
[ |
|||
console writeLine("Hello!"); writeLine("Guess what?"). |
|||
var s := console readLine. |
|||
until (s == "nothing") |
|||
[ |
|||
console writeLine("Guess what?"). |
|||
s := console readLine |
|||
] |
|||
]. |
|||
## 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 |
|||
literal myString. |
|||
// a constructor |
|||
constructor new(literal s) |
|||
[ |
[ |
||
myString := s. |
|||
] |
] |
||
// a method |
|||
printString |
|||
[ |
|||
console printLine(myString). |
|||
] |
|||
} |
|||
program = |
|||
[ |
|||
// creating a class instance by sending new message to the class |
|||
var myClass := MyClass new("This is printed by my class."). |
|||
myClass printString. |
|||
]. |
|||
The output will be: |
|||
This is printed by my class. |
|||
A nested class containing only one _eval_ method can be declared as a closure: |
|||
*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.") |
|||
] |
|||
print |
|||
[ |
|||
console printLine("I'm a Parent Class.") |
|||
] |
|||
} |
|||
class MyChild :: MyParent |
|||
{ |
|||
constructor new |
|||
<= new; // calling the parent constructor |
|||
[ |
|||
console printLine("Child Constructor.") |
|||
] |
|||
print |
|||
[ |
|||
// calling the parent method |
|||
$super print. |
|||
console printLine("I'm a Child Class.") |
|||
] |
|||
} |
|||
program = |
program = |
||
[ |
[ |
||
var myClass := MyChild new. |
|||
myClass print. |
|||
]. |
]. |
||
The output is: |
|||
Finally we may import system namespace: |
|||
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 |
|||
{ |
|||
sealed $printPrivate |
|||
[ |
|||
console printLine("private print.") |
|||
] |
|||
printPublic |
|||
[ |
|||
console print("Calling from public print - "). |
|||
// $self is a reference to the current object |
|||
$self $printPrivate. |
|||
] |
|||
} |
|||
program = |
program = |
||
[ |
[ |
||
// Note that if the constructor explicitly is not declared |
|||
console writeLine:"Hello World" . |
|||
// the system'Object one (without input parameters) is inherited |
|||
var myClass := MyClass new. |
|||
myClass printPublic. |
|||
myClass $printPrivate. |
|||
]. |
]. |
||
The output is: |
|||
Calling from public print - private print. |
|||
mytest'MyClass : Method mytest#printPrivate not found |
|||
Call stack: |
|||
system'Exception#class.new$system'LiteralValue[1]:exceptions.l(125) |
|||
system'MethodNotFoundException#class.new$system'Object$system'Message[2]:exceptions.l(213) |
|||
system'#inline1BF.start[1]:win32_app.l(252) |
|||
mytest'program.#invoke[0]:test.l(24) |
|||
system'BaseFunction.eval[0]:control.l(172) |
|||
system'#inline1BF.start[1]:win32_app.l(37) |
|||
system'startUp(5) |
|||
### 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: |
|||
import extensions. |
|||
class MyClass |
|||
{ |
|||
int _x. |
|||
int x = _x. // get accessor |
|||
set x(int o) // set accessor |
|||
[ |
|||
_x := o. |
|||
] |
|||
} |
|||
program = |
|||
[ |
|||
var myClass := MyClass new. |
|||
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** attribute: |
|||
import extensions. |
|||
class MyClass |
|||
{ |
|||
int prop x :: _x. |
|||
} |
|||
program = |
|||
[ |
|||
var myClass := MyClass new. |
|||
myClass x := 2. |
|||
console printLine("MyClass.x=", myClass x). |
|||
]. |
|||
## Exception Handling |
|||
We may use try-catch statement to handle the possible exceptions: |
|||
import extensions. |
|||
import system'io. |
|||
program = |
|||
[ |
|||
try(console printLine(File new("notexistingfile.txt"); content)) |
|||
{ |
|||
on(Exception ex) |
|||
[ |
|||
console printLine("Unknown error - ",ex). |
|||
] |
|||
on(IOException ex) |
|||
[ |
|||
console printLine("I/O error - ",ex). |
|||
] |
|||
} |
|||
]. |
|||
The output is : |
|||
I/O error - Cannot open the file |
|||
Call stack: |
|||
system'Exception#class.new$system'LiteralValue[1]:exceptions.l(125) |
|||
system'io'IOException#class.new$system'LiteralValue[1]:io\ioexceptions.l(10)system'io'FileStream#class.new$system'WideLiteralValue$system'IntNumber$system'IntNumber$system'IntNumber$system'IntNmber[5]:io\win32_files.l(52) |
|||
system'io'FileStream#class.openForRead[1]:io\win32_files.l(29) |
|||
system'io'StreamReader#class.new[1]:io\win32_files.l(207) |
|||
system'io'fileControl.reader[1]:io\win32_files.l(269) |
|||
system'io'File.read$system'text'TextBuilder[1]:io\files.l(59) |
|||
system'io'File.content[0]:io\files.l(33) |
|||
mytest'program.#invoke[0]:test.l(6) |
|||
system'BaseFunction.eval[0]:control.l(172) |
|||
system'#inline1BF.start[1]:win32_app.l(37) |
|||
system'startUp(5) |
|||
== See also == |
== See also == |
Revision as of 16:22, 6 February 2018
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 **program** symbol in the project root namespace:
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*).
program = [ 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).
Several message operations can be done in a single statement separated by a semicolon:
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:
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 literal constant value.
The assigning value can be an expression itself:
program = [ console writeLine("Hello!"); writeLine("How are you?"). var s := console readLine. ].
ELENA is a dynamic language and in normal case we may not specify the variable type:
program = [ var s := "Hello". console writeLine(s).
s := 2. console writeLine(s). ].
The output will be:
Hello 2
But it is still possible to specify the variable expected type:
type<LiteralValue> s := "Hello". console writeLine(s).
- where system'LiteralValue is a class representing text as a sequence of UTF-8 characters.*
We may use a class alias to simplify the code:
literal s := "Hello". // literal is a LiteralValue alias console writeLine(s).
Note that despite it we may still assign the objects of different types without a compile-time error:
literal s := "Hello". s := 2.
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:
literal s := "Hello". s := 2 literal.
It is guaranteed that the result of **literal** message is an instance of LiteralValue, so if the object supports the message the conversion will be done quietly. In fact this code will work because **IntNumber** supports this conversion. But the following code will be broken in run-time:
int n := 3. n := 3.4r.
The output will be:
system'RealNumber : Method $system'IntNumber not found Call stack: system'Exception#class.new$system'LiteralValue[1]:exceptions.l(125) system'MethodNotFoundException#class.new$system'Object$system'Message[2]:exceptions.l(213) system'#inline1BF.start[1]:win32_app.l(252) mytest'program.#invoke[0]:test.l(5) system'BaseFunction.eval[0]:control.l(172) system'#inline1BF.start[1]:win32_app.l(37) system'startUp(5)
- Basic Types
- The Boolean Type
Boolean type is used in conditional operations and may accept only two Boolean literals - **true** and **false**.
import extensions. 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. 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
- 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!*.
import extensions. 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.
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$system'LiteralValue[1]:exceptions.l(125) system'OutOfRangeException#class.new[0]:exceptions.l(149) system'LiteralValue.getAt$system'IntNumber[1]:memory.l(1191) mytest'program.#invoke[0]:test.l(8) system'BaseFunction.eval[0]:control.l(172) system'#inline1BF.start[1]:win32_app.l(37) system'startUp(5)
We may use another class representing UTF-16 text (**WideLiteralValue**) to solve this problem:
import extensions. 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. program = [ var s := "Привет". console printLine("The first character of ",s," is ", s enumerator; firstMember). console printLine("The last character of ",s," is ", s enumerator; lastMember). ].
The output will be correct for any UTF-8 text:
The first character of Привет is П The last character of Привет is т
- Array type
It is possible to declare a dynamic or static array.
import extensions. program = [ var staticArray := (1,2,3). var dynamicArray := Array new(3). dynamicArray[0] := 1. dynamicArray[1] := "b". dynamicArray[2] := 2.3r. console printLine("static array ",staticArray). console printLine("dynamic array ",dynamicArray). ].
The output is:
static array 1,2,3 dynamic array 1,b,2.3
- Basic arithmetic operations
ELENA supports basic arithmetic operations with integer and floating-point numbers:
import extensions.
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
- Conditions, Multi-select, Loops
Conditional statement in ELENA are defined as follows:
if(<Boolean expression>) [ /* doSomething if TRUE*/ ]; [ /*doSomehting if ELSE*/ ].
We could omit else part
if(<Boolean expression>) [ /*doSomehting if TRUE*/ ].
Usually Boolean expression is a result of a comparison operation:
program = [ console writeLine("Hello!"); writeLine("How are you?"). var s := console readLine. if(s == "good") [ console writeLine("Me too") ]; [ console writeLine("What happends?") ] ].
Several conditions can be checked:
program = [ console writeLine("Hello!"); writeLine("How are you?"). var s := console readLine. if((s == "good") || (s == "fine")) [ console writeLine("Me too") ]; [ console writeLine("What happens?") ] ].
A switch statement can be implemented using => operator:
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:
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 :
program = [ console writeLine("Hello!"); writeLine("Guess what?"). var s := console readLine. until (s == "nothing") [ console writeLine("Guess what?"). s := console readLine ] ].
- 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 literal myString. // a constructor constructor new(literal s) [ myString := s. ] // a method printString [ console printLine(myString). ] } program = [ // creating a class instance by sending new message to the class var myClass := MyClass new("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.") ] print [ console printLine("I'm a Parent Class.") ] } class MyChild :: MyParent { constructor new <= new; // calling the parent constructor [ console printLine("Child Constructor.") ] print [ // calling the parent method $super print. console printLine("I'm a Child Class.") ] } program = [ var myClass := MyChild new. myClass print. ].
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 { sealed $printPrivate [ console printLine("private print.") ] printPublic [ console print("Calling from public print - ").
// $self is a reference to the current object $self $printPrivate. ] } program = [ // Note that if the constructor explicitly is not declared // the system'Object one (without input parameters) is inherited var myClass := MyClass new. myClass printPublic. myClass $printPrivate. ]. The output is:
Calling from public print - private print. mytest'MyClass : Method mytest#printPrivate not found Call stack: system'Exception#class.new$system'LiteralValue[1]:exceptions.l(125) system'MethodNotFoundException#class.new$system'Object$system'Message[2]:exceptions.l(213) system'#inline1BF.start[1]:win32_app.l(252) mytest'program.#invoke[0]:test.l(24) system'BaseFunction.eval[0]:control.l(172) system'#inline1BF.start[1]:win32_app.l(37) system'startUp(5)
- 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:
import extensions. class MyClass { int _x. int x = _x. // get accessor set x(int o) // set accessor [ _x := o. ] } program = [ var myClass := MyClass new. 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** attribute:
import extensions. class MyClass { int prop x :: _x. } program = [ var myClass := MyClass new. myClass x := 2. console printLine("MyClass.x=", myClass x). ].
- Exception Handling
We may use try-catch statement to handle the possible exceptions:
import extensions. import system'io. program = [ try(console printLine(File new("notexistingfile.txt"); content)) { on(Exception ex) [ console printLine("Unknown error - ",ex). ] on(IOException ex) [ console printLine("I/O error - ",ex). ] } ].
The output is :
I/O error - Cannot open the file Call stack: system'Exception#class.new$system'LiteralValue[1]:exceptions.l(125) system'io'IOException#class.new$system'LiteralValue[1]:io\ioexceptions.l(10)system'io'FileStream#class.new$system'WideLiteralValue$system'IntNumber$system'IntNumber$system'IntNumber$system'IntNmber[5]:io\win32_files.l(52) system'io'FileStream#class.openForRead[1]:io\win32_files.l(29) system'io'StreamReader#class.new[1]:io\win32_files.l(207) system'io'fileControl.reader[1]:io\win32_files.l(269) system'io'File.read$system'text'TextBuilder[1]:io\files.l(59) system'io'File.content[0]:io\files.l(33) mytest'program.#invoke[0]:test.l(6) system'BaseFunction.eval[0]:control.l(172) system'#inline1BF.start[1]:win32_app.l(37) system'startUp(5)
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