Saturday, May 31, 2014

Conditional branching

ELENA like Smalltalk does not support any special language constructs to implement the conditional branching. Instead special Boolean symbols (system’true and system’false) are used. All conditional operations should return these symbols as a result.

There are three branching methods :

then[1] , then&else[2], else[1] 

(m == 0) then:
[
   n + 1
]
&else: [
   m + n
].

Note that code in square brackets are in fact nested action classes ( an action class is a class supporting evaluate message). So this code is can be written in this form:

(m == 0) then: 
{
   eval
   [
      ^ n + 1.
   ]
}
&else: 
{
   eval
   [
      ^ m + n.
   ]
}.

This expression can be written using special operators as well

(m == 0) 
  ? [ n + 1 ]
  ! [ m + n ].

Note: the main difference between using explicit messages and conditional operators is that the compiler may optimize the resulting code in the later case.

We could omit true or else part

(m == 0) 
  ! [ m / n ].

Boolean symbols supports basic logical operations (AND, OR, XOR and NOT), so several conditions can be checked

(aChar >= 48) and:(aChar < 58)
? [
   theToken += aChar.
]
! [
   #throw Exception new:"Invalid expression".
]

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 expression brackets should be used

(x >= 0)and:[ array@x != 0] ?
[
    ...
]

A switch statement can be implemented using => operator

^ aBulls =>
   -1 ? [ consoleEx writeLine:"Not a valid guess.". ^ true. ]
    4 ? [ consoleEx writeLine:"Congratulations! You have won!". ^ false. ]
    ! [
         theAttempt += 1.
                 
         consoleEx 
            writeLine:"Your Score is " : aBulls : " bulls and " : aCows : " cows".
                 
         ^ true.
     ].

Saturday, May 17, 2014

ELENA 2.0:Code blocks

ELENA code block consists of a sequence of statements. The block is enclosed in square brackets and may contain nested sub code blocks (which in fact are inline action classes). The statement terminator is a dot.

#method printAckermann &n:n &m:m
[
    control forrange &int:0 &int:n &do: (&int:i)
    [
        control forrange &int:0 &int:m &do: (&int:j)
        [
            ...
            
            console writeLine.
        ].
    ].
]

When a method should return a result (other than self) return statement is used. It should be the last statement in the block.

[
    ...

    ^ aRetVal / anArray length.
]

If the code block contains only return statement the simplified syntax can be used:

#method Number = convertor toReal:theValue.    

or there is an alternative block expression

[ convertor toReal:theValue ]

Note: it should not end with the terminator symbol

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.

#var aRetVal := Integer new:0.

Tuesday, May 6, 2014

ELENA 2.0:Classes, Roles and Symbols

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

A class encapsulates data (fields) with code (methods) to access it. In most cases it is not possible to get a direct access to the class content (it makes sense for dynamic languages when in the most cases code is generic and can be applied for different "types"). Usually the fields refer to another classes and so on until we reach "primitive" ones which content are considered as raw data (e.g. numeric or literal values).

To work with the class we have to create its instance with the help of the special methods - constructors. A constructor is used mostly to initialize the class fields. There are special type of classes which do not have fields and constructors and can be used directly (roles).

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 redirect handler (so called "horizontal inheritance"). When the parent is not provided the class inherits directly system'Object (the super class).

#class BaseClass
{
  #field theField1.
  #field theField2.
  
  #method field1 = theField1.

  #method field2 = theField.

}

#class DerivedClass : BaseClass
{
  #constructor new &field1:aField2 &field2:aField2
  [  
     theField1 := aField1.
     theField2 := aField2.
  ]

  #method add &field1:aField2 &field2:aField2
     = MyClass new &Field1:(theField1 + aField1) 
                   &Field2:(theField2 + aField2).
}

To create a class instance we have to send a message (usually new) to its symbol (a class symbol is declared implicitly for every class and can be used as a normal one)

#var anObject := DerivedClass new &field1:1 
                   &field2:1. // DerivedClass is a symbol

Roles cannot have constructors and their symbols can be used directly

#class(role)ClassHelper
{
   #method sumOf:anObject1:anObject2
      = anObject1 add &field1::anObject2 &field2::anObject1.
}

...

#var aSum := ClassHelper sumOf:anObject1:anObject2.

In general the symbol is a named expression and can be used to declare initialized objects, constants, reusable expressions and so on.

#symbol ZeroClass = DerivedClass new &field:0 &field:0.

A static symbol is the class instance which state is preserved. There could be only one instance of static symbol.

#static SingletonClass = DerivedClass new &field:0 &field:0.