Friday, March 30, 2012

Getting Started: ELENA DSA Script, part 2

In the previous post I showed how to create a pure dynamic code combining different group objects with each other without actually writing the code. So Hello world program will look like this:

&eval &nil sys'dynamics'castvariable 
  &nil sys'dynamics'batch
     &sys'vm'routines'ereturnprop "Hello World" &wrap 
        ~sys'dynamics'group_member ^append
     &write &nil 'program'output &wrap 
       ~sys'dynamics'group_member ^append
     ~sys'dynamics'group_member ^append 
  &get &nil 'program'input &wrap
  ~sys'dynamics'group_member ^append 
&wrap &nil ^eval

which is equivalent to the following ELENA code:

__wrap(__eval, __cast(
         __batch(__wrap(sys'vm'routines'ereturnprop, "Hello World"), 
                 __wrap(__write, 'program'output)),
         __wrap(__get, 'program'input))) eval.

In this example we use another group object - cast. A cast collection repeats a received message for every its member.

Inline script is not intended for direct usage so we should use a grammar. We could use a grammar script provided in the language - default.vl2

Let's start VM terminal

elt

Now we may load existing grammar scripts with a command - c

-cscripts\default.vl2

Now let's start a VM

.start

Our program consists of two actions - printing a message and waiting for any key. So let's assign a variable output and input to these actions

output = [ sys'vm'routines'ereturnprop->"Hello world" 
            write->'program'output ]
input = get -> program'input

And combine them into the program

action = < output input >
program = eval->action

So now we could execute our program

~program::eval

As you see with a help of the grammar our program becomes much simpler. The grammar is quite simple

a -> b 

creates a wrap,

[ ... ] 

creates a batch,

< ... > 

create a cast

variable =  

assigns the expression to the variable, and

obj::verb 

sends a message verb to obj

Monday, March 19, 2012

Getting Started: ELENA DSA Script

In the previous tutorial I showed the usage of experimental VM script engine. The main purpose of the script is to create a dynamic code without recompiling the application. But there is a disadvantage: the script is imperative rather than object-oriented. Now I will show how could we create a new code by combining different group objects with each other - actually assembling the object.

But before we start I will shortly describe the group / role types we will use:

As you may remember a message consists of a subject and a verb. The subject should be declared explicitly. Every time a new subject is declared the appropriate symbol is declared as well (it is used to invoke this subject). On the other hand the verb is built-in. Starting from 1.6.13 for every verb there is a special role (e.g. for get - __get) which can be used to convert this verb to "invoke" one and vice versa.

Wrap is a special group which uses the first member of the collection as a wrapper around the second one (content). It means that only the wrapper methods are invoked but "self" variable is assigned to the wrap content (so it could be called as a temporal mutation). We will use wraps with subject symbols and verb roles.

Let's practice with wraps. First of all we will need to start VM terminal. As I told in the previous tutorial VM should be start so we could use a simple script to do so - newconsole. Let's go to BIN folder and type the following command:

elt.exe -Ninlinescripts\newconsole.vl

Now I will show how to use wrap to invoke subject - for example a string length (std'dictionary'length):

-Ninline &nil 'program'output 
   &std'dictionary'length  "abc" &wrap &nil ^get ^write

This is similar to the following "traditional" code:

'program'output << __wrap(std'dictionary'length, "abc") get.

Let's try to understand how the code is working.

The wrap group object redirects the message (GET in our case) to its first member (std'dictionary'length) while assigning SELF variable with its second member ("abc") - actually wraps a literal constant in a subject role. The subject role in its turn appends the appropriate subject (std'dictionary'length) to the sent message (GET) and redirects it to SELF ("abc"). As a result

__wrap(std'dictionary'length, "abc") get

is translated into

"abc" std'dictionary'length'get

Now let's use a verb role to make our code completely dynamic

-Ninline &nil 'program'output 
    &get &std'dictionary'length "abc" &wrap &wrap &nil ^invoke 
^write

which is similar to

'program'output << 
   __wrap(__get , __wrap(std'dictionary'length, "abc")) invoke.

In this code we add an additional wrap with GET verb role and use a generic verb - invoke. When a verb role receives INVOKE message it converts it to GET and redirects to SELF - the second wrap. As a result we have a previous case.

Now let's look at another group object - BATCH. Batch a is collection which redirects sent message to every its member, passing the result of the previous operation as a parameter of the next one.

Let's once again modify our sample:

-i &nil sys'dynamics'batch 
   &get &std'dictionary'length  "abc" &wrap &wrap 
      ~sys'dynamics'group_member ^append 
   &write &nil 'program'output &wrap 
      ~sys'dynamics'group_member ^append 
&nil ^invoke

which is similar to

__batch(__wrap(__get , 
    __wrap(std'dictionary'length, "abc"), 
    __wrap(__write, 'program'output)
) invoke

Unlike the wrap, a batch does not have a script engine built-in identifier so we have to use sys'dynamics'Batch class to create a collection. ~sys'dynamics'group_member ^append is used to append the created wrap to the batch.

Our batch collection consisting of two wraps: the one we use to get the literal length and a new one, which is used to send the parameter to the console output. So let's look into our code once again. We send INVOKE message with NIL as a parameter to the batch. The batch redirects the message to its first member which returns the literal length and then sends INVOKE again to the next member but this time the parameter is a literal length. The wrap converts INVOKE into WRITE and redirects it to the console output as a result we prints the literal length.

Monday, March 12, 2012

Getting Started: ELENA Script

Now let's try to write a simple script.
First of all let's discuss what is ELENAVM script. Any ELENA source code is compiled into bytes codes (ecodes) and interpreted by a virtual machine (ELENAVM). It is the case even for a stand-alone application. So ecodes could be considered as a low level code and objects written in ELENA as a high level one. But above them there is another layer - ELENAVM inline script. It is in fact set of instructions how to assemble the objects in the virtual machine memory. With it help we could interact with ELENAVM directly. And finally we could add another layer which we could call ELENA script - it is a grammar rules how to generate ELENAVM inline script basing on a user defined syntax (DSA rules).
+++++++++++++++
+    ELENA    +
+    script   +
+++++++++++++++
+   ELENAVM   +
+    inline   +
+    script   +
+++++++++++++++
+    ELENA    +
+ source code +
+++++++++++++++
+   ecodes    +
+++++++++++++++
+ native code +
+++++++++++++++
The simplest way to work with ELENAVM is to call a VM terminal - ELT.
First of all let's start VM:
-Ninline @config win32_vm_console2 @start
(command "-Ninline" executes ELENAVM inline script directly without using any grammar; @config loads the project template; @start starts VM)
Now we could directly print the welcome message:
-Ninline &nil 'program'output "Hello World" ^write
Let's try to understand how it works. To print the message we have to load 'program'output symbol and a string constant into the stack and then send a message (the similar we did in the previous tutorial). A symbol call expects a parameter in the stack (or nil if it is not required), that's why we put &nil symbol first (note: "&" symbol indicates a constant and therefore it is not a call but writing into the stack). Then the literal constant follows (writing into the stack as well). Now we have a two objects in VM stack - 'program'output (actually sys'io'consoleoutput) and std'basic'wideliteral - and can invoke a message (the message is sent to the previous stack item with the current one as a parameter) with a command ^ write. After the message is executed the parameter is removed from the stack.
Alternatively we could provide a grammar rules to parse a user defined script (ELENA script). Let's start with defining a simple context free grammar rules (only aA, a, A, AB and $eps rules are allowed).
-mcf
start ::= "?" print;
print ::= $literal;
(Note: $literal is a terminal mask accepting any literal constant)
Then we need a DSA rule to build proper inline script.
print => &nil 'program'output $terminal $body ^write;
(Note: $body is place holder where appropriate rule content is inserted - in our case it is a literal constant, $terminal defines the place where the parsed value should be inserted ).
Now let's end the CF grammar definition mode
-moff
Now try simple expression:
? "Hello World!!"
To see how script is translated into the VM instructions let's turn on tracking mode
-ton
And repeat the command:
? "Hello World!!"
We will see the code similar to our previous one:
@push $elena'nil
@call 'program'output
@push "Hello World!!"
@send write
Let's turn the tracking off
-toff
The script engine can be used to execute programs as well.
First let's provide the path to our module:
-Ninline @use "..\examples\helloworld"
And now let's execute it:
-Ninline &nil helloworld'program &nil ^eval
(Note: the program should be already compiled)
Our inline script is the same as a previous one except that we do not need a parameter that's why nil is used second time as well.

Friday, March 9, 2012

Getting Started: Hello World

This tutorial will discuss how to write a simple program.

First of all we have to create a new project - File - New - Project.

The project setting dialog will be displayed. Let's fill the form:

  • Type - win32console (it is a console application)
  • Target file name - helloworld (it is a name of the executable file)
  • Debug mode - Enabled (if we would like to have a possibility to debug the code)

The rest can be leaved blank.

Now let's create a program file - File - New -Source File.

And finally let's save all changes - File - Save All.

IDE will ask to include the file into the project.

Our program will be simple:

Program =>
[
 'program'output << "Hello word!!%n".
].

But we still have to tell the compiler which symbol should be used as a program body. To do so we have to provide the forward reference - Project - Forwards.

In open forward dialog we have to type the entry mapping:

'program'action = helloworld'program

And press Add and Save.

Now let's compile the project - Project - Compile.

We could run the program - Debug - Run.

After the program is finished the console window is closed so we do not see actually the program output. So let's add the code to wait until any key is pressed:

Program =>
[
 'program'output << "Hello word!!%n".
 'program'input get.
].

Now let's take a look at our code. 'program'output symbol is a console text writer supporting basic standard types: literal and numeric values. Leading apostrophe indicates that this a forward declaration. The forward symbol should be resolved during the program linkage. This reference is resolved in win32_console project template and is mapped to sys'io'consoleoutput. 'program'input is a console text reader.