Tuesday, November 8, 2011

What's new in 1.6.6

In this post I would like to discuss some things done in 1.6.6. The migration of ELENA code base to LIB2 (and simultaneously to GCX ) was finally completed. Up'N'Down sample (the most complicated program written in ELENA so far) is now playable again. As a result I was able to continue work on ELENA library. So I would like to present two new classes (actually external roles) introduced in 1.6.6: std'patterns'esort and std'routines'estrop.

First of all I have to remind some basic things about roles and groups. A role is an alternative set of methods which can be used for context-dependant programming. The role cannot have fields so it could be considered as stateless class. There are two types of roles: internal and external ones.

A group is a collection of objects sharing the common instance variable (self). The group is used for dynamic code extending. We could consider it either as a JIT (just in time) inheritance or as a code mutation. For example if I would like to add or override the object method I could extend it by creating a group with my custom object containing the required code. The given group will be practically identical to the original object and could be used instead of it.

So if we would like to apply the role to the object we could actually group them. For internal role it is done with a help of #shift statement. For external we should declare the group ourselves with tilde operator (in reality it is a bit more complicated but I will simplify for the sake of clarity).

So far so good. Now let's turn to our new classes (roles). std'patterns'ESort is used to extend any collection / array with sorting functionality. Here some examples how it could be used:

#var a := (1,7,4,-5).
a~esort run: aPair => (aPair former < aPair later).

#var b := ("b","ab","a","aa").
b~esort run: aPair => (aPair former < aPair later).

#var c := list += 3 += -4 += 1.
c~eindexeditem~esort run: aPair => (aPair former < aPair later).

#var d := list += "b" += "av" += "a".
d~eindexeditem~esort run: aPair => (aPair former < aPair later).

Note that std'routines'EIndexedItem extends any collection with "@" operator (used for array-like access to its members). Run method sorts the collection basing on the provided expression.

Our another role - std'routines'EStrOp extends the literal value with some basic string operations like find, insert, delete and substring.

#var p1 := "abba"~estrop find &first:"b".
#var p2 := "abba"~estrop find &last:"a".

#var s2 := "abba"~estrop replace &from:"b" &to:"B".
#var s3 := "abba"~estrop replace_all &from:"b" &to:"B".
#var s4 := "abba"~estrop substring &from:1 &to:2.

#var s41 := "abba"~estrop substring &from:1 &length:2.

#var s5 := "aa"~estrop insert &literal:"bb" &index:1.
#var s6 := "abbba"~estrop delete &from:2 &to:2.

Monday, October 3, 2011

ELENA Engine: Sections, references

When I started developing my language I was interested in creating a compiler from scratch without looking into other implementations. So some of my solutions may look unorthodox. Nevertheless the linker is the most "old" part of my code. Once made that decision I stuck with it.

Any compiler may be split into several parts: a parser, a code compiler and a linker. In general a parser parses the source code, a compiler generates the output code and a linker assembles this code into executable file. From the linker point of view the program code consists of sections connected with each other by references. The section may contain both an executable code and data. The output sections usually are saved into temporal files, which we could call compiled modules (or simply modules).

In ELENA the situation is a bit more complex. The modules may contain native executable code (in this case this module is called "primitive" and generated by external tools, for example asm2binx) and ELENA byte codes (ecodes). So ecodes should be converted into native codes with a help of Just In Time Compiler (JITCompiler & JITLinker in ELENA Engine). It could be done in the compiler (elc) itself or in a virtual machine (elenavm) right after the module is loaded (hence the name - just in time compiler).

Now let's discuss ELENA compiled module (.NL file). We could consider the module as a list of data (sections, messages, constants). To help the linker find the required data, every list item has a unique (within its module) identifier - a reference. A reference is a 32bit integer, where the highest byte contains the reference type (see elenascr2\engine\elenaconst.h :: ReferenceType) and the rest is a reference number. The reference number has a corresponded literal identifier in the module reference table - reference name. The reference name consists of module and proper names. If the module name equals to the name of the current one it is the reference to this module, otherwise it is external one.

The code and data are saved into sections. Depending on the reference type the section may contain native code, data, VM byte codes, VMTs. In most cases the section has references to other part of the program or library. To support these external references every section is followed by a relocation table. The relocation table has quite simple structure: it is list of reference id and the address of the reference location in the section (relative to the section).

The module may contain messages (actually message qualifier - subjects) and constants. Every time a new subject is declared its name is saved in the message table and the appropriate reference is used inside the code. With the help of relocation table these ids are synchronized between different modules. The same happens with constants (numeric and literal). This allows us to have only one instance for every constant used in the code.

So how the linker works? Every program has an entry (or several of them in case of VM). Presume it is "sys'entries'simple". The linker loads the module "sys'entries" and finds the appropriate reference number (based on the module reference table). To load the required data we need to know the reference type. Presume it is a symbol reference (byte code section). Combining the reference number and the type we could find the required section. The reference type tells the linker where this section should be copied to, in our case to .TEXT (executable code). If it is a byte code (as in our case) JITCompiler converts ecodes into the native commands. Next the linker will go through the relocation table and load all referred data (sections, external functions, messages, constants and so on) and update the section body with the correct addresses and so on.

And finally I will provide the structure of .NL file:

ELENA module structure
-------------------------
a) General file structure:
+--------------+
| module stamp |
+--------------+
| module name  |
+--------------+
|  references  |
+--------------+
|   messages   |
+--------------+
|  constants   |
+--------------+
|   sections   |
+--------------+

b) module stamp - fixed-size module version signature (not terminated by zero)

c) module name  - zero terminated module name

d) references   - reference section 
+--------------+
|    size      |  total section size
+--------------+ 
|   reference  |
|     memory   |
|  hash table  |

e) messages
+--------------+
|    size      |  total section size
+--------------+ 
|   message    |
|   memory     |
|  hash table  |

f) constants
+--------------+
|    size      |  total section size
+--------------+ 
|   constant   |
|    memory    |
|  hash table  |

g) sections     - section list      

                 
{section}

where section = 
<section id>
<section size>
<section body>

<relocation table size>
<reference id>
<reference position>
...

Tuesday, September 20, 2011

ELENA Virtual Machine Script: BF interpreter

In the previous post I showed some basic things about the terminal script. After several changes the current system of commands looks like this:

  • -q - quit
  • -h - help
  • -ton - trace mode is on
  • -toff - trace mode is off
  • -l[path] - load a script from file
  • -n[path] - load an inline script from file
  • -c[path] - load DSA script from file
  • -i[inline script] - execute inline script\n"
  • -g[dsa rule] - define dsa rule; they should be separated by a semicolon except the last rule\n"
  • -pX - select parse mode (0 - standard, 1 - simple)

Note that there are two parse modes: standard and simple. Presume we have to parse the following expression: "23 * 4". In Standard mode it will be parsed as three terminal symbols: "23", "*" and "4". But in simple mode every character will be a separate terminal symbol. This mode will be used in our BF interpreter.

Despite I called it interpreter it is actually just-in-time compiler. The script tape generated by ELENA Script Engine is translated into byte-codes and executed like "normal" code.

The DSA script looks like this:

statement  ::= next;
statement  ::= next;
statement  ::= prev;
statement  ::= inc;
statement  ::= dec;
statement  ::= out;
statement  ::= in;
statement  ::= "[" while;
statement  ::= idle;

next       ::= ">";
prev       ::= "<";
inc        ::= "+";
dec        ::= "-";
out        ::= ".";
in         ::= ",";
idle       ::= $any;

while      ::= statement while_r;

while_r    ::= "]";
while_r    ::= statement while_r;

tape       ::= statement statements;

statements ::= statement statements;
statements ::= $eps;

start      ::= tape;

tape       =>  &union
                  &group 1024 += &std'dictionary'count += *=
                  &group { 0 std'basic'widecharvar } += &std'basic'factories'onnew += *=
               std'basic'factories'newinitializedarray 0 ^refer $body;
next       => 1 &std'dictionary'index ^^append $body;
prev       => 1 &std'dictionary'index ^^reduce $body;
inc        => 1 ^append $body;
dec        => 1 ^reduce $body;
out        => &nil 'program'output &previous ^write . $body;
in         => &nil 'program'input &nil ^get ^write $body;

while      => &group
                &previous +=
                &std'patterns'ewhilenotzero +=
                { $body } ^run .;

As you may already know the script consists of two type of rules: grammar and DSA ones. Grammar rules allow us to generate a derivation tree and DSA ones are used to build the code (list of VM instructions, so-called a script tape) based on that tree. DSA rule is applied for the grammar one with the same name.

I think grammar rules are quite simple so we begin with DSA ones. The first rule which is invoked right after the start is tape. In this rule we create an array of widechar objects.

tape       =>  &union
                  &group 1024 += &std'dictionary'count += *=
                  &group { 0 std'basic'widecharvar } += &std'basic'factories'onnew += *=
               std'basic'factories'newinitializedarray 0 ^refer $body;

The equivalent ELENA code will look like this:

std'basic'factories'newinitializedarray::&count:1024 
&onnew: (=> std'basic'widecharvar::0) @ 0

Note that { 0 std'basic'widecharvar } is an action symbol (compare with => std'basic'widecharvar::0 in ELENA code). To pass several parameters to newinitializedarray we use a union of group objects.

&group 1024 += &std'dictionary'count += *=

is in fact equivalent to

{ count = 1024. }

where std'dictionary'count is a subject symbol, &group is an instance of the $elean'group class and command += adds the object to the group.

To understand how it works I will remind about subject symbols. Every time a new subject is declared the appropriate subject class is auto-generated as well. This class contains "subject'get" method returning self variable. A union is a week group type. It means that self variable points to the group member rather than to the group itself. So the following expression

#union(#group(std'dictionary'count,1024),#group(std'basic'factories'onnew,...)) std'dictionary'count

will return a count group. As a result this union of groups is equvivalent to

{ std'dictionary'count = 1024. std'basic'factories'onnew = ... }

Other rules are quite simple. For example rule next:

next       => 1 &std'dictionary'index ^^append $body;

is similar to

... index'append:1

Note that &std'dictionary'index is a subject symbol used as multiple dispatcher: ^^ is equivalent to &: operator.

And finally let's consider the simple echo program:

-p1
,[.,]

It will be translated into the following inline code:

@new $elena'$union
@new $elena'$group
@push 1024
@group-add
@push std'dictionary'count
@group-add
@union-add
@new $elena'$group
@sub
  @push 0
  @call std'basic'widecharvar
@end
@group-add
@push std'basic'factories'onnew
@group-add
@union-add
@call std'basic'factories'newinitializedarray
@push 0
@send refer
@push $elena'$nil
@call 'program'input
@push $elena'$nil
@send get
@send write
@new $elena'$group
@copy 1
@group-add
@push std'patterns'ewhilenotzero
@group-add
@sub
  @push $elena'$nil
  @call 'program'output
  @copy 1
  @send write
  @pop
  @push $elena'$nil
  @call 'program'input
  @push $elena'$nil
  @send get
  @send write
@end
@send run
@pop

Monday, August 1, 2011

ELENA 1.6.3 released

ELENA 1.6.3 is out now

This release includes several language enhancements beta version of ELENA script engine, refactored LIB2 and beta version of GCX. All samples (except upndown) are migrated to lib2.

Other changes

  • @ operator parsing order is changed
  • #define import subjects as well
  • argument list is now possible to use with symbol parameter as well
  • nil subject is no longer supported, use nillable
  • several bug fixes

Friday, July 15, 2011

LIB2:Conversion to the string

Being a pure object-oriented language ELENA does not support any built-in conversion mechanisms, objects should implement special methods themselves. A standard way to make the object convertible to the string is to implement literal'get method

#class MyLiteral
{
...

#method literal = "My Class".
}

...

#var aLiteral := MyObject literal.

If the object is a number it could implement a numeric protocol. In this case the code will look like this:

#class MyNumber
{
...

#method numeric = 5.
}

...

#var aLiteral := anObject numeric save: std'basic'WideStrConvert.

Note: std'basic'WideStrConvert converts supported numeric values (integer and real numbers) to the literal one.

Let's use more generic code. In this case we have to implement a conversion protocol.

#class MyLiteral
{
...

#method literal = "My Class".

#method request : anObject = anObject literal:($self literal).
}
#class MyNumber
{
...

#method numeric = 5.

#method request  :  anObject = anObject save:($self numeric).
}

...

#var aLiteral := MyObject request:{ 
literal : aLiteral = aLiteral

numeric : aNumber = aNumber save:std'basic'WideStrConvert.
}.

Note: request method is used to define the object supported "type" (either numeric, literal, indexable and so on).

Alternatively we could use existing symbol std'routines'PrintValue:

#var aLiteral := std'routines'PrintValue::MyObject.

In case if our object is a complex one let's consider a last example (see goods sample)

#class Record
{
#field theName.

#field thePrice.

...

#method literal
[
^ ext'text'TextWriter write:"Name:" write:theName write:" Price:" write:thePrice literal.
]
}

...

'program'output << MyRecord.

Note: if PrintValue symbol is used (e.g. in 'program'output) request method may be omitted if the object implements literal'get one.

Saturday, July 9, 2011

"Weekly" release - 1.6.2.1

This is a weekly service release. Note it is binary incompatible with the previous versions

  • -ffriend-injection mingw compiler option was removed
  • subject implementation overhaul
  • #define imports subjects as well
  • nil subject is no longer supported, use nillable
  • vm terminal refactored
  • fixed bug in asm2binx: __arg1 is not recognized as a reference for SUB
  • @ operator parsing order is changed
  • argument list is now possible to use with symbol parameter as well
  • script engine refactored

You may download it from sourceforge

Monday, June 27, 2011

Subject symbols

As I mentioned in the very first post in this blog when I started ELENA project I decided that the best plan is no plan. After all it is hard to plan if you have only a vague idea of your future programming language. After ten years of development several distinct languages were developed (just compare 1.0.0, 1.4.6, 1.5.1 and 1.6.2) and the work goes on. So when people (those few who bother to look at) complain that ELENA is a weird or exotic they forget that it is an experimental language and it shouldn't be all shiny (at least until it is in development phase). Its main purpose is to check the proposed concepts. And at the moment it is an open architecture concept.

This model of development may be called evolutional (yes, in the evolution there is no plan as well). Similar to the real evolution some minor tactical decisions may have a great impact. When I came up with an idea of a message subject I tried to solve a practical question: how to write a real program if you have very limited set of message names. Later it appeared that the subject can be used to implement some kind of multiple dispatching in ELENA. And now (starting from 1.6.3) the subject will be used in VM script engine as a message adapter.

Every time a new subject is declared the appropriate implicit symbol is auto-generated as well (actually two symbols). When the symbol is called without an argument it returns an external role (stateless object) used to invoke a message, otherwise the property wrapper.

So let's look at several use cases. The first case is sending a message. Presume we would like to send a qualified message to the object. Usually it looks like this - anObject verb &subject:aParameter. With the help of the subject symbol we could implement the same code without using subject (remember it is not possible to use subjects in ELENA-script): anObject~subject verb:aParameter. In this code our subject symbol translates verb into subject'verb.

Secondly let's extend an object with a subject property: anObject~(subject::Variable), subject symbol returns a wrapper object around Variable which translates subject'get / subject'set into get / set.

Let's look how this concept can be used in vm terminal. Presume we would like to return the literal constant length: nil 'program'output "abc" nil nil std'dictionary'length &get will print 3.

One may ask why not to allow to use subjects in the script? Of course it could be easily done but in this case there is no need in subjects at all. We could allow methods to have any names the programmer wants and in the result we will get ... Smalltalk. The need to formalize the class interface was one of the main reasons I started this project. Only time will tell if this approach is feasible but at the moment you may consider this as a basis assumption on which the language is built.

P.S. The discussed functionality will be implemented in the upcoming weekly release (probably in two week after 1.6.2 release). In the next post I will display how symbol subject can be used to implement bf interpreter.

Sunday, June 26, 2011

ELENA 1.6.2 released

ELENA 1.6.2 is out now

This release includes alpha version of ELENA script engine, changes in the language syntax, new library lib2(only console samples are migrated).

Starting from 1.6.2 the migration to a new library LIB2 began. At the moment all console and rosetta stone ones are migrated to LIB2

The work on GCX (a revised garbage collection) continues. Young and old generations are introduced.

ELENA Script Engine is introduced. At the moment it is very early alpha version.

Other changes

  • it is possible now to switch to another role inside another one
  • run-time multi-dispatching
  • new argument option: nullable
  • obsolete argument option: norecc
  • #continue is no longer supported
  • new statement: #try
  • some basic byte code optimization rules
  • new compiler options: g0, ox
  • some basic byte code optimization rules

Friday, June 10, 2011

ELENA Virtual Machine Script

The last year in the article The Next Big Thing I mentioned the intention to create a special VM script to manipulate with the objects. It took me half a year to come up with the possible solution. And though it is a very early alpha a new tool introduced in the current release (bin\elt.exe) put this idea into practice.

So what is ELENA script? First of all it should not be confused with a traditional script language, it has not its own interpreter, grammar, libraries. It is pure a set of instruction for ELENA VM to load existing symbols into the memory and send them messages. So its prime goal is to assemble objects in VM memory with the help of special rules (if it is done from ELENA program it could be considered as a self assembling, hence its name - DSA (dynamic self assembling) rule). The possibility to work with your own objects directly without need to write a special test program could be useful as well. I hope it will find an application for code generations, open architecture systems (as a way to build complex group objects) and so on.

The actual system consists of VM terminal (elt.exe) and a special script engine (elenasm.dll) working with ELENA Virtual Machine (elenavm.dll). It is designed to be used directly from ELENA program as well (of course if the program is VM client). There are actually two types of script: inline one (which can be used directly with VM without need for the script engine) and grammar driven (where context-free and DSA rules modify the script text to create an inline one, so it could be considered as a additional layer between the client and VM).

Before I will show how it looks to work with VM terminal I would like to notice that at the moment it is very early alpha version and it is still unstable. Secondly direct script requires some knowledge of ELENA stack machine: a symbol reference should be always preceded by a parameter (if there is no parameter, nil constant should be used), the same is true for the message call. Thirdly only generic messages can be used (without a subject qualifier; actually it was designed for such a use from the beginning). Note also, that the current implementation allows to work only with console applications. There should be a special VM client for GUI applications. And finally it is presumed that you works with LIB2 programs (if you need to use LIB, please change libpath in elt.cfg to ..\lib)

So let's start the terminal. Inline script should be executed with an option "-i"

First of all we need to start a virtual machine and load default template

-i @config win32_vm_console2 @start
The canonical hello world sample will be looked like this:

-i &nil 'program'output "Hello World!!" ^write

Let's print the sum of two numbers:

-i &nil 'program'output 2 3 ^add ^write

Now let's do a bit more complex task and call existing application (it should be compiled before). First of all you have to tell VM where your compiled modules are located

-i @use "..\examples\binary"

And now let's execute the program symbol:

-i &nil binary'program &nil ^evaluate

As you see it is not very convenient way to write the expression (but it could be ok for computer programs). So let's try to use some grammar. I would like to note that the script engine support simple context free grammar. There are several restriction: only one terminal symbol (it should be first) and non-terminal (with except of AB type rule) can be used. So only the following type of rules are allowed:

  • S::=aA
  • S::=a
  • S::=A
  • S::=AB
  • S::=$eps

So let's define our simple rule

-g print ::= "?" expression;
-g expression ::= $literal;

(Note $literal is a special terminal mask accepting any literal constant)

DSA rules are used to provide required symbols and operations.

-g print => &nil 'program'output $body ^write;

(Note $body indicates the place where rule content is inserted)

Now let's mark print as a start rule

-g start ::= print

Let's test if our code works

? "hello world!!"

You could load existing scripts as well

-c..\examples\opencalc\opencalc.vl2

And now you may calculate some basic arithmetic expression

?2*3+4*5

You may look how your expression is translated into direct script with the following command -ton

-ton
?2*3+4*5

You will see the script listing. To turn the tracing off type the following

-toff

To exit the program, type

-q

That's all I would like to show in this tutorial. In near future I will show some more complex samples (for example I will implement bf interpreter)

P.S. Note that in the current implementation several key features are lacking, for example it is not possible to declare variables. I'm going to implement them in the recent releases. Once again it is still unclear if this turns into something more than a nice toy but it is the direction ELENA Program Language Project is moving to.

"Weekly" release 1.6.1.2

This is a weekly service release.

  • optimization: direct method call for constants and external roles
  • new statement: #try
  • bsort migrated to lib2
  • fixed bug with inline class argument hint
  • vm terminal
  • script engine

You may download it from sourceforge - ELENA 1.6.1.2

Saturday, May 28, 2011

LIB2: Tutorial, part 1

As work continues on LIB2 more samples are moved to it. So it's time to take a look at the changes the new library brought with it

#symbol Program =>
[
'program'Output << "Hello World!!%n".

'program'input get. // wait for any key
].

As you can see little was changes: a generic message GET is used instead of CHAR'GET. So let's look at more complex sample: words

#define std'basic'*.
#define std'collections'*.
#define std'patterns'*.
#define ext'text'*.
#define ext'patterns'*.
#define ext'routines'*.

#subject std'dictionary'*.

#subject total_words, unique_words.

// --- WordList ---

#class WordList
{
#field theList.
#field theTotal.

#method new
[
theList := List.
theTotal := Integer::0.
]

#method += aWord
[
theTotal += 1.

#if (ListSearching::theList ==aWord)?
| [ theList += aWord literal. ].
]

#method total_words = theTotal.

#method unique_words = theList count.

#annex (theList).
}

// --- Program ---

#symbol Program =>
[
'program'output << "Enter the text(to stop press enter two times):%n".

#var aFlag := Boolean << True.
#var aText := TextWriter.
#var aWriter := #annex(aText)
{
<< aLiteral
[
#if (aLiteral length == 0)?
[
aFlag << False.
]
| [ aText << aLiteral. ].
]
}.

#loop aFlag?
[
'program'Input >> aWriter.

aWriter += " ".
].

#var aList := WordList.

WordScan::aText run: aWord => (aList += aWord).

'program'output << "There are " << aList unique_words << " unique words out of " << aList total_words.
'program'output << "%nthe list of unique words:%n".

aList~CollectionPrinter save:'program'output.

'program'input get. // wait for any key
].

The first thing that catch an eye is a possibility to use symbol names without a namespace qualifier or an alias. New modules (like ext'routines) and classes (ext'text'TextWriter) were introduced. ext'patterns'WordEnum was renamed to WordScan. An external role ext'routines'CollectionPrinter is used to print the collection. Qualified messages unique_words and total_words are used instead of private ones $getTotal and $getUnique. Apart from these the sample logic remains the same: with the help of wordscan the entered text is split into words and only unique words are saved in the collection; the resulting list is printed at the end.

Friday, May 27, 2011

"Weekly" release 1.6.1.1

This is a weekly service release. It contains several major changes in ecodes(complete overhaul of ELENA bytecode opcodes, making this release incompatible with 1.6.1), some changes in group support, multiple dispatching. The migration to LIB2 continues, several samples were moved to it

  • it is possible now to switch to a role inside another one
  • new ecode: snop
  • run-time multi-dispatching
  • obsolete argument option: norecc
  • new argument option: nullable
  • intsum & realsum migrated to lib2
  • binary migrated to lib2
  • new compiler option - g0
  • ecodes are simplified, several big commands are split
  • #continue is no longer supported
  • words migrated to lib2
  • some basic byte code optimization rules
  • new option to turn on / off optimization - ox

You may download it from sourceforge - ELENA 1.6.1.1

The next several posts will discuss what's new in LIB2 comparing with the LIB

Thursday, May 5, 2011

ELENA 1.6.1 released

ELENA 1.6.1 is out now

This major release includes changes in the language syntax, several critical bug fixes and enhancements in the language library code.

1.6.1 is not binary compatible with the previous versions (due to changes in ecodes and a grammar)

multi-threading support

GCX (a revised garbage collection) is now thread-safe

changes in ecodes (ELENAVM byte codes)

  • several “big” commands are split, to reuse existing ecodes (disptach, redirect)
  • new ecode – popn

simplified syntax to create a group

instead of old style syntax

#group(events'EHandler, BtmSave) += …
it is possible to use the following syntax
BtmSave~events'EHandler += ..

#define command without an alias

it is possible to include another namespace without providing a namespace shortcut

#define std'basic'*.

Integer << 2.

new compiler hint syntax

instead of old style

#hint(subj:mysubject)
#class MyClass {…}
a new syntax is introduces
#class(subj:mysubject)MyClass {…}

#hint is no longer supported

the new hint syntax should be used

several major changes in an expression syntax

The following syntax

A verb:B::C
should be replaced with
A verb:(B::C)

An argument list could be used in initializing expression
A::&arg1:B &arg2:C
The following code do not need an extra brackets any more
#inline alias(A, B, C) verb:D

several bug fixes

  • a bug in memory management
  • a #group statement bug for x platform
  • cannot compile class with roles in the release mode bug
  • #00058: duplicate subjects
  • #00059: win32_gui_vm_client and win32_console_vm_client applications do not work
  • #00060: #var a := a verb:b circular referencing
  • upndown bug:player plays with a joker as a biggest one
  • bug: 64bit integer numbers are displayed incorrectly in the debugger watch window

lib2

  • the work on an alternative system library is began. LIB2 will replace existing one after 1.7.0
  • hellowworld_u sample migrated to lib2

IDE

IDE is now ready to debug multi-threading applications

Friday, April 22, 2011

Weekly release - 1.6.0.3

After quite a long period of time I finally made a next release.

This time it includes several major changes in ELENA byte-codes and syntax.

- Several "big" bytecode commands (like redirect and dispatch) are split into smaller ones to reuse existing commands. It means 1.6.0.3 is not binary compatible with previous versions.

- It is possible now to include other symbol namespaces without aliases. I.e. instead of old code:
#define basic'* = std'basic'*.

...

#var anObject := basic'True.

it is possible to write the following:
#define std'basic'*.

...

#var anObject := True.

- The simplified syntax to create a group. Compare old code
           #group(events'EHandler, BtmOk) += 
{ on'Click'process = self set &dialog_result:basic'false. }.

with a new one:
           BtmOk~events'EHandler += 
{ on'Click'process = self set &dialog_result:basic'false. }.


- There is a simplified syntax for providing compiler hints:
#method(disp) += anObject
[
...
]

The work on an alternative library (lib2) is started. It will eventually replace the existing one starting from 1.7.0

Tuesday, April 5, 2011

Current status

It has been a while since my last post and a last release. So I decided to post a little update on the project status.

At the moment I'm working on introducing a new concept to the language - external role (or ELENA protocol).

As you probably remember a role is an alternative VMT. But I realized that there could be an external one. It can be considered as a short-time mutation as well. The good thing is that unlike a normal mutation (either #annex or #group) it does not introduce any performance overload. As a result I will implement an alternative API (so called API2), located in lib2 (the source code in src2).

So how it will be used? For the programmer there will be nothing new. For example, we've implemented a protocol __address. To use it we have to write the following: anObject1 __address ifequal:anObject2.

anObject1 actually may not know anything about the protocol (though in most cases it should implement some methods to support the protocol).

The next release will contain the first implementation of the protocol concept

Friday, March 18, 2011

Weekly release - 1.6.0.2

This is a weekly service release. It contains several major bug fixes.

- fixed #group statement bug for x platform
- fixed critical error: cannot compile class with roles in the release mode
- fixed gui'controls'Combobox-items append method doesn't work with a literal value

As a part of the compiler development (x project) the work on multi-threading support was continued. IDE is now fully ready for debugging several multi-thread applications;
new modules:sys'threading, win32'api'threading.

Friday, March 4, 2011

Rosseta code tutorials:Arithmetic evaluation

Today, let's consider another rosetta code sample - Arithmetic evaluation

We have to evaluate an arithmetic expression by building the parsing tree. So let's define tree elements being used by the parser:

#subject parse_order.

// --- Token ---

#class Token
{
    #field theValue.
    
    #method parse_order'get = 0.
    
    #method += aChar
    [
        theValue += aChar.
    ]
    
    #method + aNode
    [
        ^ aNode += self.
    ]
    
    #method new
    [
        theValue := String.
    ]
    
    #method numeric = theValue save:Real64Convertor.
}

// --- Node ---

#class Node
{
    #field theLeft.
    #field theRight.
    
    #role Empty
    {
        #method += aNode
        [
            theLeft := aNode.
            
            $self $setLeftAssigned.
        ]
    }
    
    #role LeftAssigned
    {
        #method += aNode
        [
            theRight := aNode.
            
            #shift.
        ]
    }
    
    #method $setLeftAssigned
    [
        #shift LeftAssigned.
    ]

    #method + aNode
    [
        #if (self parse_order > aNode parse_order)?
        [
            self += aNode.
        ]
        | [
            aNode += self.
            
            ^ aNode.
        ].
    ]
        
    #method += aNode
    [
        #if (theRight parse_order > aNode parse_order)?
        [
            theRight += aNode.
        ]
        | [
            theRight := aNode += theRight.
        ].        
    ]
    
    #method new
    [
        #shift Empty.
    ]
}

// --- SummaryNode

#class SummaryNode (Node)
{
    #method parse_order'get = 2.
    
    #method numeric = theLeft numeric + theRight numeric.
}

// --- DifferenceNode ---

#class DifferenceNode (Node)
{
    #method parse_order'get = 2.
    
    #method numeric = theLeft numeric - theRight numeric.
}

// --- ProductNode ---

#class ProductNode (Node)
{
    #method parse_order'get = 1.
    
    #method numeric = theLeft numeric * theRight numeric.
}

// --- FractionNode ---

#class FractionNode (Node)
{
    #method parse_order'get = 1.
    
    #method numeric = theLeft numeric / theRight numeric.
}

Our tree will consist of Token instances (leaf nodes) and Node descendants (branch node). Any branch branch may have two children. So the expression 1+2+3 will be presented like this:

       --(+)----
      |         |
  -- (+)--     [3]
 |        |
[1]      [2]

To correctly parse the expression like 1+2*3 any node should return its precedence level (parsing order). That's why we declare a new property (actually new subject) - parse_order. The tree is evaluated by sending real'get message to the top node, which in turn sends it to its children until we reach terminal node (Token). Each brunch node (SummaryNode, DifferenceNode and so on) performs the associated operation with the results and at the end we've got the result of the expression.

Let's consider some parts of the code. Token's numeric'get method converts the literal value to a numeric one:
theValue save:Real64Convertor
Add message (operator +) is used to connect two nodes with each other in accordance with their precedence levels. To add children nodes to the brunch one append message (+= operator) should be used. Note the use of roles there. Instead of checking each time if the sub node was already attached we define two roles - Empty and LeftAssigned. Due to current implementation limitation we cannot switch from one role to another one inside the role so a private method is used ($setLeftAssigned).
To support precedence-control parentheses we have to declare a special node - SubExpression. It is actually a sub tree. Note, we have to use Parser class before its declaration. It can be done by providing the symbol namespace (if our module is named as arithmeval - arithmeval'Parser).
#class SubExpression
{
    #field theParser.
    #field theCounter.
    
    #role EOF
    {
        #method eof'is []
        
        #method += aChar [ $self fail. ]
    }
    
    #method parse_order'get = 0.
    
    #method + aNode
    [
        ^ aNode += self.
    ]
    
    #method append : aChar
    [
        #var aCode := Int32Value::aChar.

        #if control if:(aCode == 41)
        [
            theCounter -= 1.
        ]
        | if:(aCode == 40)
        [
            theCounter += 1.
        ].
        
        #if(theCounter == 0)?
            [ #shift EOF. ^ $self. ].
        
        theParser evaluate:aChar.
    ]
    
    #method numeric = theParser numeric.
    
    #method new
    [
        theParser := arithmeval'Parser.
        theCounter := Integer << 1.
    ]
}
The field theCounter is used to deal with nested parentheses. Now let's declare our parser class.
#class Parser
{
    #field theToken.
    #field theTopNode.
    
    #role Start
    {
        #method evaluate : aChar
        [
            #if (40 == aChar)?
            [
                theToken := SubExpression.
                theTopNode := theToken.
                
                $self $setBrackets.
            ]
            | [
                theToken := Token.
                theTopNode := theToken.
                
                theToken += aChar.
                
                #shift.
            ].
        ]
    }
    
    #role Brackets
    {
        #method evaluate : aChar
        [
            theToken += aChar.
            
            #if theToken eof'is
            [
                #shift.
            ].
        ]
    }
    
    #role Operator
    {
        #method evaluate : aChar
        [
            #if Control if:(48 < aChar) if:(58 > aChar)
            [
                theToken := (Token += aChar).
                
                theTopNode += theToken.
                
                #shift.
            ]
            | if:(40 == aChar)
            [
                theToken := SubExpression.
                theTopNode += theToken.
                
                #shift Brackets.
            ]
            | [ $self fail. ].
        ]
    }
    
    #method numeric = theTopNode numeric.
    
    #method evaluate : aChar
    [
        #if Control if:(48 < aChar) if:(58 > aChar)
        [
            theToken += aChar.
        ]
        | if:(42 == aChar)  // *
        [
            theTopNode := theTopNode + ProductNode.
            
            #shift Operator.
        ]
        | if:(47 == aChar)  // /
        [
            theTopNode := theTopNode + FractionNode.
            
            #shift Operator.
        ]
        | if:(43 == aChar)  // +
        [
            theTopNode := theTopNode + SummaryNode.
            
            #shift Operator.
        ]
        | if:(45 == aChar)  // -
        [
            theTopNode := theTopNode + DifferenceNode.
            
            #shift Operator.
        ]
        | if:(40 == aChar)
        [
            theToken := SubExpression.
            theTopNode := theToken.
            
            #shift Brackets.
        ]
        | [ $self fail. ].
    ]
    
    #method new
    [
        #shift Start.
    ]
    
    #method $setBrackets
    [
        #shift Brackets.
    ]
}
Parser is an action class used by a literal enumerator. Evaluate method is called for every expression character to build the parser tree. Once again the roles are used to deal with special cases (like parentheses).
Parser class gives us an opportunity to speak about conditional statements in ELENA. Because the language does not have any built-in types it is problematic to implement classical conditional statements. One possible solution is implemented in Smalltalk where closures are used (the similar could be done in ELENA as well). The second solution used by the language takes into account another ELENA feature - alternative flow: if the object does not react to the message (no appropriate record in VMT) the flow is considered to be broken and control goes to the alternative one. So the conditional statement in the language looks like this:
#if anObject message1:aParameter1 // first condition
[
   ....
]
| message2:aParameter2  // second condition (optional)
[ 
   ... 
]
|    // else condition (optional) 
[
   ...
].
Though conditional statement can be used with any message, operators ? (is) and ! (isnot) are used in ELENA API. It is presumed that a true value (std'basic'True symbol) reacts to ? operator and a false one (std'basic'False) to ! operator. So the conditional statement may look like this
#if aFlag?
[
   // true part
]
| ! [
   // false part
]
| [
    // error part
].
Comparison operators (==, !=, >, < and so on) returns either true or false values (or fails if the operands are not compatible). So we could write like this
#if (32 < aChar)?
[
   ...
].
Note that we compare an integer to the character rather than other way around - to convert character value to a numeric one.

If several conditions are required we could use 'and' and 'or' messages (supported by boolean symbols)
#if (48 < aChar) and:(58 > aChar)
[
].
Note, that in this case both condition are evaluated (unlike C++ for example). But it is still possible to achieve using std'patterns'Control symbol
#if Control if:(48 < aChar) if:(58 > aChar)
[
]
In this case if the first condition is not true the control goes immediately to the alternative part (if presented).
And finally, the conditional statement (as well as a loop one) does not break the flow if the message fails and there is no alternative statements (as opposed to a normal statement). That why we have to break it ourselves (if we want to inform the method caller that the operation is failed).
#if Control if:(48 < aChar) if:(58 > aChar)
[
   theToken += aChar.
]
...
| [
   $self fail.
].
It is presumed that fail message is never handled.
#symbol Program =>
[
    #var aText := String.
    
    #loop ((Console >> aText) length > 0)?
    [
        #var aParser := Parser.

        Console << "=" << aText then: aText =>
        [
            Scan::aText run:aParser.
            
            ^ aParser numeric.
        ]
        | << "Invalid Expression".
        
        Console << "%n".
    ].
].
The main program body is quite simple. We read the user input, parse it and prints the result (or an error message if the expression is invalid) until the user enters the empty line. ctrl'Control-then method evaluates the action (closure; a la Smalltalk) and returns the result.

Monday, February 28, 2011

Rosetta Code: Arrays

Today, let's discuss the work with array (In this post I combined several tasks).
First, lets' show the basic array syntax
There are several ways how to declare the array in ELENA. The simplest way looks like this
#var anArray := (1, 2, 3).
Note that the array is an object containing the references to the integer objects rather than values itself. By default the array object inherits std'basic'Array class.
The syntax to retrieve the element is quite simple:
#var anItem := anArray@1.
Note that @ message returns the indexer proxy rather than the element itself. If you wish to retrieve the object itself the code should be a bit more complex:
#var anItem := (anArray@1) content.
To find out the difference between these variants let's examine the concept of an indexer. The indexer is a special adapter allowing to access the array members. To create indexer it is enough to send indexer message
#var anIndexer := anArray indexer.
The indexer can retrieve or assign the current array element with content'get / content'set methods (note that set method works only for dynamic arrays) and navigate the array (it is similar to the C array pointer). So to retrieve the element we have to create an array, move to the required position and return the content:
anArray indexer write &index:1.
But it is possible to use more simple way with the help of "refer" message (@ operand).
anArray @ 1.
Let's consider the following sample:
ctrl'It::anArray run : anItem =>
[
   'program'Output << anItem << "%n".
]
In this code we print every member of the array. Note that the object is extended with its index.
ctrl'It::anArray run : anItem =>
[
'program'Output << "a[" << anItem index << "]=" << anItem << "%n".
]
We could use an enumerator as well. In this case the object rather then the indexer is used:
ctrl'Scan::anArray run : anItem =>
[
   program'Output << anItem << "%n".
]
If we would like to get the original object from the proxy (for example if it should be assigned to the class field, in this case it is not optimal to store the proxy) we should use content'get message:
anArray@1 content.
Now let's see how could we create a dynamic array:
#var anArray := basic'NewArray::3.
In most cases there is no difference between the constant and the dynamic one except assigning the value:
anArray@0 set &content:2.
I hope it is clear now why we have to do it this way. After a dynamic array is created all its members are nil. Though it is possible to create and fill the array at the same time
anArray := factory'NewArray::{ &factory'array_size:3 &factory'pattern:(Integer::0) }.
And finally let's consider the code to calculate the mean(arithmetic average) of a numeric vector.
#define std'basic'*.
#define std'patterns'*.
#define std'dictionary'*.

// --- Sum ---

#class MeanAction
{
    #field theValue.
    #field theCount.
    
    #role Empty
    {
        #method numeric'get = 0.
        
        #method evaluate : aValue
        [
            theValue := Real::0.
            theCount := Integer::0.
            
            #shift.
            
            self evaluate:aValue.
        ]
    }
    
    #method new
    [
        #shift Empty.
    ]
    
    #method numeric'get = theValue / theCount.
    
    #method evaluate : aValue
    [
        theCount += 1.
        
        theValue += aValue.
    ]
    
    #method start : aPattern
    [
        aPattern run:self.
        
        ^ self numeric.
    ]
}

// --- Program ---

#symbol Program =>
[
    'program'Output << MeanAction start:Scan::(1, 2, 3, 4, 5, 6, 7, 8).
].
In short we execute the special action object for every member of the collection (or an array) and then print the result. The program code is quite simple (once again we use an enumeration, but this type with implicit action object instead of anonymous one), so let's look more precisely at MeanAction. Enumeration code pattern executes the action for every member of the collection by sending "evaluate" message (=> operator) with the collection member. So we have to declare this method and calculate the total sum simultaneously counting the numbers. "numeric'get" method returns the array mean. In the object constructor we initialize the action fields. Till this moment all is quite straightforward. But there is a special case - zero vector (an array with zero members). If we try to return the mean for it the program will crash with divide by zero exception. So we are creating a special set of methods (Empty role) to deal with it and switch the object to it (#shift Role). If evaluate method is called at least once than the vector is not zero and we have to use the standard set of methods (statement #shift).

Friday, February 25, 2011

Rosseta code tutorials:Amb operator

Practically from the very start I had a problem with finding interesting samples to be implemented on ELENA. And only recently I found a place where I could find them

So I'm continuing implementing these tasks. My next sample is Amb operator.

Let's consider several tasks - one (with literals) from rosetta code (i.e. it is a failure if the last character of word 1 is not equal to the first character of word 2, and similarly with word 2 and word 3, as well as word 3 and word 4) and another (with numeric) from here. We will call them Program1 and Program2.

#symbol Program1 =>
[
#var A := AmbValue::(1, 2, 3).
#var B := AmbValue::(4, 5, 6).

AmbOperator::(A, B) seek: => (A * B== 8).

'program'Output << A << "*" << B << "=8".
].

#symbol Join =
{
if &first:aFirst &second:aSecond
[
^ aFirst@(aFirst length - 1) == aSecond@0.
]
}.

#symbol Joinable : aPair = Join if:ctrl'Args::aPair.

#symbol Program2 =>
[
#var A := AmbValue::("the","that","a").
#var B := AmbValue::("frog", "elephant", "thing").
#var C := AmbValue::("walked", "treaded", "grows").
#var D := AmbValue::("slowly", "quickly").

AmbOperator::(A, B, C, D) seek:
=> (Joinable::(A,B) and:Joinable::(B,C) and:Joinable::(C,D)).

'program'Output << A << " " << B << " " << C << " " << D.
].


How does Amb operator work? At first we have to define set of possible values for amb operator to choose from (AmbValue). Then we ask AmbOperator to find a correct subset. Joinable symbol is used for a tricky condition of the rosetta code sample (a@(a length)==b@0). ctrl'Args symbol is used to simplify the symbol argument list (i.e. Join if:ctrl'Args::(a, b) is similar to Join if:{ first'get = A. second'get = B. } or Join if:(&first:A &second:B)).

So let's consider the code to implement Amb operator.
We start with a multi-list enumerator. I would like to remind that an enumerator is used in ELENA to execute an action for every member of a collection (similar to foreach statement in C#). Why we need an enumerator? To find a correct solution AmbOperator has to go over all possible combinations of amb operands. So let's implement it (it is declared in ext'patterns module):
#class MultiEnumerator
{
#field theEnumerators.

#role BOF
{
#method proceed
[
#shift.

ctrl'Control run &list:theEnumerators &foreach:anEnumerator => (anEnumerator proceed).

^ basic'True.
]
}

#method enumerator = $self.

#method proceed
[
#var aRetVal := ctrl'Control run &list:theEnumerators &foreach:anEnumerator =>
[
#if(anEnumerator proceed)?
[ ^ basic'False. ].

anEnumerator clear proceed.
].

^ (basic'nilValue != aRetVal).
]

#method new : aCollection
[
#shift BOF.

theEnumerators := basic'ArrayType evaluate &__array &count:(aCollection count) &filling: aCurrent => (aCollection@(aCurrent indexer) enumerator).
]

#method get = nil.
}

There is nothing special there. In the construct we create a list of enumerators for every member of the parameter. There is a special use case at the beginning (role BOF) where all enumerators are initialized (first call of proceed method). With each next call of proceed method the first enumeration goes on until it reaches the end, then it is repeated for the next member of the second enumeration and so on until the last enumeration is finished.

Now we need AmbValue enumerator:
#class AmbEnumerator
{
#field theValues.

#role BOF
{
#method proceed
[
theValues $reset.

#shift.

^ basic'True.
]
}

#method new : Values
[
theValues := Values.

#shift BOF.
]

#method proceed
[
^ theValues $next.
]

#method clear
[
#shift BOF.
]

#method get = theValues.
}

AmbEnumerator is actually an adapter which dynamically modify (mutate) AmbValue by calling private methods - $reset (AmbValue is wrapped around the first member of its collection) and $next (AmbValue is wrapped around the next one).
#class AmbValue
{
#field theValues.
#field theCurrent.

#method enumerator = AmbEnumerator::self.

#method $reset
[
theCurrent := theValues@0.
]

#method $next
[
#var anIndexer := theCurrent indexer.

anIndexer += 1.

^ anIndexer eof'is back:basic'False | back:basic'True.
]

#method new : Values
[
theValues := Values.
theCurrent := nil.
]

#union (theCurrent primary).
}

As I said before AmbValue is a proxy class (dynamic extension) over the collection of possible values. Only one value at the time can be accessible (statement #union). "Primary" message is used to return the actual object (note that theCurrent is an indexer).

And finally our AmbOperator
#symbol AmbOperator : Values =
{
seek : anExpression
[
ctrl'Control run &enumerator:exctrl'MultiEnumerator::Values &foreach: aCurrent =>
[
^ anExpression evaluate inverted.
].
]
}.

So how does it work? The key is AmbValue. With a help of ELENA magic AmbValue could be wrapped around one of its members.
    #var A := AmbValue::(1, 2, 3).
#var B := AmbValue::(4, 5, 6).

At the beginning A and B points to nil objects. But if we will call $reset method they will be equal to 1 and 4 integer constants (actually are wrapped around them, bit for other parts of the program they ARE integer numbers). After we call $next method they will be 2 and 4. Others are quite simple. AmbOperator with the help of MultiEnumerator executes the required expression for every possible combination of A and B values (i.e. 1 and 4, 1 and 5, 1 and 6, 2 and 4 and so on) until the expression is true (A * B = 8). Et voila!

P.S. The code works for ELENA API starting from 1.6.0.1 (I'm going to release it very soon). I added exctrl'MultiEnumerator and fixed several bugs in indexer implementation (without them the code for Join symbol should be like this: (aFirst literal)@(aFirst literal length - 1) == (aSecond literal)@0.).

Friday, January 28, 2011

Rosseta code tutorials:Add a variable to a class instance at run-time

Another Rosetta code sample here

Though ELENA does not support adding a function / variable at a run time this could be simulated with the help of a group object (Note that ELENA does not allow a direct access to the object fields at all).

So we have to create a group member FieldContainer which will hold the variable and a pair of the methods to access it (get / set).

First of we have to declare a new subject (a property name) - foo
#subject foo.

Then a property container:
#class FieldContainer
{
#field theValue.

#method foo'set : anObject
[
theValue := anObject.
]

#method foo'get = theValue.
}

So now we could extends any existing object
// adding a field
anObject := #group(anObject, FieldContainer).

anObject set &foo:"bar".

#var aConstant := #group("My String", FieldContainer).
aConstant set &foo:"boo".

'program'Output << anObject foo.

Rosseta code tutorials:Ackermann function

Let's implement Ackermann function - Rosettacode.

Because ELENA is a pure object-oriented language we have to create the object which will calculate the function. This object will only evaluates the parameter so we have to declare symbol with the single method "evaluate" (note that a new class can be declared both as implicit symbol - #class MyClass {} and as an explicit one -
#symbol MyClass = { ..}, the only difference that the symbol cannot have fields and be inherited).

#symbol Ackermann =
{
    a_function &m:anM &n:anN
    [
        #if anM
            ifequal:0 [ ^ anN + 1. ]
            | greater:0 ?
            [
                #if anN
                    ifequal:0 [ ^ self a_function &m:(anM - 1) &n:1. ]
                    | greater:0 ? 
                    [ 
                        ^ self a_function 
                             &m:(anM - 1) 
                             &n:(self a_function &m:anM &n:(anN - 1)). 
                    ].
            ].
        
        $self fail.
    ]
}.

The implementation is quite straight-forward but there are several things I would like to explain. Any ELENA method has only one parameter but it is still possible to pass several ones using the argument list (which is in fact the single object which returns the expected arguments). The argument names have to be declared beforehand
(see - Method calling syntax).

Let's declare the required subjects.

#subject a_function, m, n.

Secondly Ackermann function is defined for n and m bigger or equal to zero. So we have to decide what to do if one of the parameters is negative. ELENA does not support exceptions so we have to break the program flow. It is done by sending the special non-existing message - fail - to itself.

self fail.

Now we could calculate our function:

'program'Output << Ackermann a_function &n:1 &m:1.

Let's modify our code using the appropriate signature (note that << and write are synonyms):

'program'Output write &ackermann &m:0 &n:3.

To use this signature we have to declare a class with a "signature" hint:

#class(signature:(a_function, m, n)) AckermanValue
{
    #field(arg:m)theM.
    
    #field(arg:n)theN.
    
    #method m'get = Int64Value::theM.
    
    #method n'get = Int64Value::theN.
    
    #method save : aWriter = aWriter write &:(Ackermann a_function:self).
    
    #method literal_writer'read : aWriter
    [
        aWriter << "A(" << theM << "," << theN << ")=" << Int64Value::self.

        ^ aWriter.
    ]
}

Note that declaring AckermanValue class we do not specify its type (ELENA is actually a type-less language but we could assign a subject to it which can be used as its "type " by multi-dispatching mechanism), so 'program'output will not know how to print it. We have to add a new method to implement a custom print action.

#class AckermanValue
{
...   
    #method save : aWriter = aWriter write &:(Ackermann a_function:self).
    
    #method literal_writer'read : aWriter
    [
        aWriter << "A(" << theM << "," << theN << ")=" << Int64Value::self.

        ^ aWriter.
    ]
}

The expression output will be the following:

A(0,3)=4

Thursday, January 27, 2011

ELENA 1.6.0 released

This release includes major language changes (method calling overhaul, simplified message argument list syntax, new classes and several critical bug fixes) as well as refactored ELENA standard library.

A new concept of method signature is introduces (reusing code, simplifying the message
sending syntax). In general the number of subjects was reduced. For example the subject content is no longer supported. The generic verbs are now used to set / get indexer / variable content.
1.5.6:
anArray@3 content'set:anObject

1.6.0:
anArray@3 indexer set:anObject


The subject bool was removed as well. Compare:
1.5.6:
#if (anInteger >= -2147483646)bool'or:(anInteger <= 2147483647)?

1.6.0:
#if (anInteger >= -2147483646)or:(anInteger <= 2147483647)?


I will post several tutorials describing 1.6.x features.

http://sourceforge.net/projects/elenalang/files/ELENA/1.6.0/ELENA-1.6.0.zip/download

Saturday, January 15, 2011

ELENA Programming Language:method calling syntax

In this post I would like to make a short overview of ELENA method syntax features (starting from 1.5.6.9 version).

ELENA programming language (unlike many others) introduces several limitation on the programmer free will over naming and using methods. First of all the method can have only one parameter. Secondly there are special rules how the method could be named (apart from private ones, which we will ignore in this post).

The simplest method declaration may look like this (in case of in-line class, the keyword is omitted):
#method subject'verb : parameter
[
]



As you see the method name consists of two parts: a subject (a noun) and a verb. The verb defines an expected operation, the subject describes a method parameter (a list of possible operations with it).
If the message do not have a subject part it is considered to be generic (and can be used for multi-dispatching). The 'GET' messages may omit their verbs. The following expressions are equivalent: “aNumber int'get” and “aNumber int” (“int” is a subject).

There are a limited list of verbs in ELENA (though this lists will be expanded with the time, to avoid possible verb overuse). Let's name them: add, and, append, back, check, clear, clone, close, delete, dispatch, divide, do, equal, evaluate, find, free, get, greater, if, ifequal, ifless, ifnot, increase, insert, is, isnot, less, load, multiply, new, not, notequal, notgreater, notless, open, or, proceed, process, react, read, reduce, repeat, rewind, run, save, send, separate, set, start, stop, subtract, wait, write, xor.

There are synonyms (operators) for some verbs. Unlike normal verbs, operators have lower parsing order. These codes are equivalent - “a << b + c int'get” and “a write:(b add:(c int'get))”. Operatiors are generic verbs and cannot have subjects. Let's name the operators and their verbs:
== - equal,
!= - notequal,
>= - notlesss,
<= - notgreater,
> - greater,
< - less,
? - is,
! - isnot,
@ - seek,
=> - evaluate,
>> - read,
<< - write,
+ - add,
- - subtract,
* - multiply,
/ - divide,
+= - append,
-= - reduce,
*= - increase,
/= - separate

Unlike verbs the subject names are not limited (it is not recommended to give a name equal to a verb, because of possible ambiguity). But the subject should be declared before the use. The syntax looks like this:
#subject subject, argument1, argument2.

It is possible to import subjects declared in another packages (the subjects declared in the project are automatically imported in all following modules).

#subject math'dictionary'*.


If there is a name conflicts between subjects declared in different modules it is possible to provide an alias (will be implemented in 1.6.0).

#subject math'* = math'dictionary'*.


It is possible to import the subjects to the whole project (std'dictionary, std'dictionary'protocols, std'dictionary'types subjects are imported automatically to any program or library).

Though the method may have only one parameter it is still possible to provide several arguments with the help of argument objects. Let's consider how we could declare the method with an argument list.

#method subject'verb &argument1:anArgument1 &argument2:anArgument2
[
]



This code will be compiled into the following:

#method subject'verb  : anArguments
[
#var anArgument1 := anArguments argument1'get.
#var anArgument2 := anArguments argument2'get.
]



The similar syntax could be used for passing multiple arguments to the method (note that it doesn't matter if the method declaration uses an argument list).

anObject subject'verb &argument1:anArg1 &argument2:anArg2.


This code will be compiled into the following:

anObject subject'verb: { argument1'get = anArg1. argument2'get = anArg2 }.


As it was already said above the subject defines the method parameter. This feature is used in ELENA for implementing multi-dispatching.

Let's declare three classes A, B, C and assigns subject1 to the class A, subject2 to the class B (class C does not have assigned subject).

#hint(subj:subject1)
#class A
{
}

#hint(subj:subject2)
#class B
{
}

#class C
{
}



Now we will declare the fourth class which will deal with them.

#class D
{
#method subject1'set : anObject [ … ]

#method subject2'set : anObject [ … ]

#method set : anObject [ … ]
}



We are going to call the verb set with instances of classes A, B and C. According to our notation for the instance of the class A we have to call subject1set, for an instance of B – subject2'set. Because the class C does not have an subject we should call generic method set (it is expected that the generic methods are able to work with “unknown” objects). So lets declare three variables and call the appropriate methods.

#var anA := A.
#var aB := B.
#var aC := C.

D subject1'set:anA subject2'set:aB set:aC.



But in most cases we do not know its type (in ELENA its class or supported subject). It is possible dynamically resolve the appropriate message. Let's add a special attribute to the generic method.

 #hint(disp)
#method set : anObject [ … ]



Now we could simplify the code and the appropriate message will be called automatically

D set:anA set:aB set:aC.


And finally let's consider the concept of an argument signature. A signature allows to define the message subject on the base of argument list (static multi-dispatching).

Let's presume we have a following argument list

#method subject'set &argument1:anArg1 &argument2:anArg2 &argument3:anArg3 […]


Instead of passing every time all three arguments we could define three alternative argument signature: &argument1 & argument2, &argument2 & argument3 and &argument1 & argument3.

#hint(subj:subject, signature:(argument1, argment2), signature:(argument2, argment3), signature:(argument1, argment3))
#class CustomArgumentList
{
#hint(arg:argument1)
#field theArg1.

#hint(arg:argument2)
#field theArg2.

#hint(arg:argument3)
#field theArg3.

#method argument1'get = theArg1 nil'isnot | back:0.

#method argument2'get = theArg2 nil'isnot | back:0.

#method argument3'get = theArg3 nil'isnot | back:0.
}



(Note that the signature class, should be declared in the same module where the first argument is declared). The mapping between argument value and a signature class field is defined by arg hint.

So now we could use simplified syntax to call subject'set

anObject set &argument1:anArg1 &argument2:anArg2.
anObject set &argument2:anArg2 &argument3:anArg3.
anObject set &argument1:anArg1 &argument3:anArg3.



Note that the order of the argument subjects play important role. &argument2 &argument1 and argument1 &argument2 are different signatures.

If the argument list contains only one argument it will be automatically translated into an appropriate message.

anObject argument1'set:aParam and anObject set &argument1:aParam are equivalent.


The signature cannot be used in the method declaration. If no appropriate signature can be found the generic method will be called.