MATRIX SCRIPT LANGUAGE

OVERVIEW


A MATRIX-script includes any logical aspects for the execution of a control task by the MATRIX. The MATRIX is a STATE-MACHINE. I.e., depending on the status of the system (the MATRIX) the reactions of the system can change on appointed occurrences - as per particulars given below. For the time being we attend to the elementary structures of a MATRIX script.

SCRIPT STRUCTURE

A MATRIX script operates like many program languages: It consists out of a initiation part, a declaration part, functions and a main function.
There are iterations, variables and levels, so called STATES. Anybody who ever programmed or scripted in any language will easily find his way. Many things retrieve and some things even in a C-like syntax.

KERNEL, DEVICES AND PLUGINS

A DEVICE announces the arriving of data to the kernel. The kernel searches the SCRIPT for an EVENT which can accept that data and processes it concerning the actions defined in the SCRIPT for this EVENT.
So, an EVENT within the script is similar to an event handler and the appearance of an event is not dissimilar to the appearance of an exception of an interrupt.

The range of functions of a MATRIX script depends on the integrated DEVICES and PLUG-INs. A DEVICE or PLUG-IN represents, as software, the functionality of a specific hard- or software ( for example a DVD-Player, the belonging DEVICE provides functions like; "Play", "Go to Track" etc.)

The same can be valid for software, e.g. a speech recognition: The speech recognition tries to identify incoming sounds from the soundcard as spoken words. Finally the result is a word recognized, or not. This functionality now can be replicated by a DEVICE. The DEVICE takes on the task to send the recognized word to the MATRIX ,or, in the case of not recognizing, to send a failure code.

The difference in principal between a DEVICE and a PLUG-IN is the way of communication with the kernel.
A DEVICE communicates via sockets with the kernel - but a PLUG-IN is linked to the runtime as DSO. There is no loss of time caused by the socket communication, but therefore a PLUG-IN always has to run on the same computer the kernel is executed on. Only DEVICES are able to work with the kernel distributed in the net.
The PLUG-IN named "matrix" is already ingrained in the kernel, hence it must not be loaded. matrix provides some functions of the kernel that are reserved just for it. The most important event is: matix:main() . As the first of all EVENTS, it will be called only a single time immediately after the start. The perfect place for initializations and suchlike.

Before using a DEVICE (and its functions) or a PLUG-In, it is necessary to tell the kernel which DEVICEs or PLUG_INs shall be applied.

 

use a device

For making it possible to work with a device within the script, the device has to be declared at the beginning of the script. In the following example the DEVICE "console" shall be declared. This DEVICE is a very simple one and is able to handle the tasks of keyboard inputs and screen outputs; in a way the "standard out" of the MATRIX.

That is the way it happens:

use con = console@localhost("inout");

use is a directive for the MATRIX, a keyword for the declaration of devices!
con is an ALIAS for the DEVICE "console" ("con" is used in the script every time the console is triggered).
= is the assignment. Therewith "con" will be understood as ALIAS for the following DEVICE.
console@localhost is the designation of the DEVICE which enables the KERNEL to find the DEVICE.
Here, @ describes the separation between the designator of the DEVICE and the designation of the computer, the DEVICE runs on (see network).
"localhost" stands for the fact that the DEVICE and the kernel of the MATRIX are located on the same computer. At the same place could stand a host name ("examle.aec.at") or an IP-address (for example 192.168.1.10). The only requirement by the KERNELs is, that the mentioned DEVICE runs from the beginning on the specified computer and that the computer is accessible via TCP/IP.
"localhost"
stands for the fact that the DEVICE and the kernel of the MATRIX are located on the same computer. At the same place could stand a host name ("examle.aec.at") or an IP-address (for example 192.168.1.10). The only requirement by the KERNELs is, that the mentioned DEVICE runs from the beginning on the specified computer and that the computer is accessible via TCP/IP.

 

The bracketed STRING (= "inout") can be used by the DEVICE as an initialization parameter.

Important: The semicolon at the end of the line! ; In general the symbol for the end of a line in the script language!

SOME MORE EXAMPLES

use tts = tts@localhost("");
use gram = gramma@voice.aec.at("");
use cave = cave@192.168.23.57("inout");

Include a Plug-In
Right after the use-assignment, still in the initialization part of the script, the includes get defined. Until now, "calc" is the most important PLUG-IN. So, we take it as example for the integration of a PLUG-IN in the script:

include c = calc("");
include The keyword for the MATRIX. This way the kernel recons that a PLUG-IN has to be integrated at this position.
C The ALIAS that is assigned to the PLUG-IN within this script.
= The assignment of the PLUG-IN for the ALIAS.

calc("");
calc is the PLUG-IN, the string within the brackets serves the purpose of compatibility with other PLUG-INs. That string (in this case an empty character string) can be used for initializing the PLUG-IN.But calc does not know any initialization.

For the scope of service of "calc" see Utility (calc) Plugin

variables types:

int Integer
float floating-point
string a sequence of CHARs (character string)
void for the declaration of a function that returns nothing

EXAMPLE 1:

int i;
float fl_var:
string myString = "My Name in my string!";
int y = 0;

functions
The keyword for the declaration of functions used in the script is:
functions
A function gets declared as following:

type name( [type variablenname [, type variablenname [, ...]]] ) 
[type local_variable_name;
[type local_variable_name;
[...]]]
{
	(ACTIONS, code, function calls, etc)
	[return value;]
}

Whereat value has to be specified in the declaration head as a type.

EXAMPLE 2

void do_some_inits( int init_number )
int i; 
{
	for (i = 0; i <= max; i = c:iadd(i,1) ) {
		test_array[i] = init_number;
	}
}

EXAMPLE 3

int do_some_inits( int init_number )
int i; 
{
	i = init_number;
	return i; 
}
Example 3 is basically senseless, but should explain the possibility of returning a value out of a function. Events An event starts with the assignment "->" in the first character string of a line. Followed by the ALIAS of the device, than a colon (":") and the chosen function of the DEVICE.

[state1 [| state2 [ ...]]]-> Device_ALIAS:function_name( parameter_1 [, parameter_N]) 
{
	(ACTIONS, code, function calls, etc)
}

the device-alias has already been declared by "use". The actual function of the DEVICE depends on the actual DEVICE.

Following: An example for an EVENT for the DEVICE "console" ALIAS "con":

EXAMPLE 4

->con:read('A')
{
	con:writeln("the KEY 'A' has been pressed");
}
the KEY <A> has been pressed


EXAMPLE 5

include c = calc("");

string st;
string key;

->con:read(^key)
{
	st = "the KEY '";
	st = c:sadd(st, key);
	st = c:sadd(st, "'has been pressed");
	con:writeln(st);
}

This EVENT is going to be executed, no matter which key on the keyboard of the computer the DEVICE "console" runs on will be pressed.
To get the same output like in example 4, the output string sending to con:writeln has to include "KEY". For that aim the example triggers the CALC-plug-in (plug-ins are DSOs, we already know).
The example adds, by the help of the CALC-plug-in, "KEY" into a string and transfers it to con:writeln.

matrix:main()
This EVENT - that is how the "main-routine" has to be understood - is a kernel function and not an actual PLUG-IN or DEVICE.

matrix:main() is the perfect place to perform initializations in, because matrix:main will be executed only a single time, as the first of all declared EVENTS, immediately after starting the program.
The one who now asks for something like the "main-loop" did not see through the MATRIX-script yet.

The kernel observes all DEVICES all the time and searches the SCRIPT for matching points of entrance. If there exists one of these points of entrance, the one will be executed. I.e. the "main-loop" already runs within the kernel and not within the SCRIPT.

Let us have a look on a first run able SCRIPT: DEMO-Script 1:

EXAMPLE 6

1	#!../matrix
2	use con = console@localhost("");
3	include c = calc("");
4		
5	# every line starting with an hash (#) at the first
6	# column will be ignored by the kernel. It is ment to 
7	# be a REMARK or Comment-Line.
8	
9	# here we declare global variables
10	int max = 5;
11	int test_array[5];
12
13	functions
14	# the word "functions" opens the functiondeclaration 
15	# space in the script!
16
17	void do_some_inits( int init_number )
18	# "do_some_inits" is a function declaration.
19	# Note, this function does not return any value!
20	# it just changes the content of the globalstructrure 
21	# "test_array"
22	int i; 
23	# functions do support local variables!
24	{
25		for (i = 0; i <= max; i = c:iadd(i,1) ) {
26			test_array[i] = init_number;
27		}
28	}
29
30	->con:read('i')
31	# everything starting with an "->" at the first column is
32	# ment to be an EVENT! An EVENT can be triggered by any 
33	# DEVICE 
34	{
35		do_some_inits( 1 );
36		con:writeln("manual init done");
37	}
38
39	-> matrix:main()
40	# this is the only EVENT which is understood by the 
41	# kernel itself the keyword "matrix" does not have to be
42	# declared (in a use command at the beginning of the 
43	# script) since matrix refers to the kernel itself.
44	# matrix:main is the main-program if you like. One can 
45	# use it but it is not obligatory.
46	{
47		do_some_inits( 0 );
48		con:writeln(" first init done");
49	}

The DEMO-script explains:
In front of every line stands a line number. They are not part of the script, but should make it easier to refer the following comments to the right line!

Line 1
can be dropped. This line makes the isolated script executable, so it does not have to be activated in the command line after the syntax: ./matrix scriptname It is sufficient to execute only the script ./scriptname

Line 2
use-commando The DEVICE console gets included and assigned to the ALIAS con.

Line 3
include The PLUG-IN calc gets linked to the running time and assigned to the ALIAS c

Line 5 to 7
Nothing but comments. Every line that starts with the character hash (#), will be ignored as comment line.

Line 10 to 11
Declaration of the scripts global variables!

Line 13:
Keyword for beginning declarations of functions!

Line 17 to 28:
Function do_some_inits gets declared. Do_some_inits occupies every cell of the global structure "test_array" with the value transferred to the function (init_number vom typ int).

Line 30 to 37:
The EVENT con:read(´i´) gets declared and an ACTION gets fixed. I.e., that every time "i" gets pressed at the console computer (here: localhost), the fixed ACTION will be executed. That is in general the call for the function do_some_inits and the transfer of the value 1 to this function. After that follows the output to the device CONSOLE (con:writeln) "Manual init done".

Line 39 to 49:
At this point the EVENT matrix:main gets declared and an AKTION gets fixed. If the EVENT happens (that happens only a single time immediately after the start!), the function do_some_inits will be activated and the value 0 will be transferred. After that the text: " first init done" will be transferred to con:writeln.

STATES

As already mentioned below, the MATRIX can also be used as a STATE-MACHINE. For the explanation of a STATE-MACHINEs concept we choose a rather abstract example; "The Four Seasons". The Four Seasons Supposed the task is to develop an installation about the topic; the four seasons. In this case we ignore the artistic quality of the argumentation and determine the following facts in order to understand the functions of STATES:

1. We have an installation the user interacts with by speech.
2. The user has influence on the weather. The MATRIX understands "warmer", "colder" and "precipitation".
3. For the output we have something like a spotlight - light / or a speaker - sound.
4. The seasons ought to be selectable via keyboard, so 1 = winter, 2 = spring etc. …

ENTER
Warmth, cold & precipitation (VOICE) Seasons (Keyboard)

ERGO
Spring, summer, fall and winter And there is the possibility of precipitation - like rain, thunder-storm, snow…

Ideal would be a system, that puts itself automatically into several STATES in order to be able to reflect the status of the seasons, if the task was to produce the considering precipitation.
The thing declared as season, can be understood as STATE and be implemented! The MATRIX "knows" which season is on and changes "rain" into "snow". The appended SCRIPT could look like the following:

EXAMPLE 7

WINTER->voice:read(^cmd) {
	if ( cmd == 1) precipitation ( snow ); 
	if ( cmd == 2) temperature_up( frost_max );
	if ( cmd == 3) temperature_down( frost_min );
}

SPRING->voice:read(^cmd) {
	if ( cmd == 1) precipitation ( rain ); 
	if ( cmd == 2) temperature_up( cool_max );
	if ( cmd == 3) temperature_down( cool_min );
}

SUMMER->voice:read(^cmd) {
	if ( cmd == 1) precipitation ( thunder ); 
	if ( cmd == 2) temperature_up( warm_max );
	if ( cmd == 3) temperature_down( warm_min );
}

AUTUMN->voice:read(^cmd) {
	if ( cmd == 1) precipitation ( rainstorm ); 
	if ( cmd == 2) temperature_up( cool_max );
	if ( cmd == 3) temperature_down( cool_min );
}

->con:read('1') {
	state(WINTER);
}

->con:read('2') {
	state(SPRING);
}

->con:read('3') {
	state(SUMMER);
}

->con:read('4') {
	state(AUTUM);
}

This SCRIPT contains four times the same EVENT voice:read. This EVENT always takes every command from the DEVICE voice (in this case the ALIAS for a speech recognition DEVICE).

Cmd = 1 signifies, that the word "precipitation" has been recognized.
Cmd = 2 signifies, that "warmer" has been recognized.
Cmd = 3 signifies, that "colder" has been recognized.

All the four EVENTS deal with any case of appearance of a CALL. Only the STATE noted in front of the "->" makes sure, that only one EVENT (the one with the valid STATE) will be executed at the same time.
The EVENTS con:read() get executed only if the correlating figure gets pressed. The assignment of these EVENTS consist of the task to change the valid STATE to the STATE referring to the figure. Because of there is nothing standing in front of the "->" of these EVENTS, i.e. they are not referring to a STATE, they keep on being valid no matter what STATE is actual at this time. In a way the con:read-EVENTS stay valid all over the year.
Going one step further, we could say that, for instance, in spring or fall should happen something that does not happen in winter or summer (for example an existing spotlight should shine only half as light as in the other seasons) and add the following supplement:

SPRING | AUTUM -> voice:read(^cmd) {
	if (cmd == 1) light:out(5);
}

WINTER | SUMMER -> voice:read(^cmd) {
	if (cmd == 1) light:out(10);
}

When ever the STATE "AUTUM" or SPRING" is valid, and somebody asks for precipitation, the light will be set on level 5. In summer and winter on level 10.

I.e., an EVENT can stay active all the time (without STATE), or only at a specified point of time (with a STATE) or even during several STATES - by connecting all the STATES with a PIPE-symbol (logical-or).

These STATES do not only make the SCRIPT more legible. Above all, the very complex structures of a inside networked system can be recreated without establishing a vast number of variables and requests (speed). But above all, the applications actions in the particular STATES stay completely independent and lead to a safe total behavior of the installation. No matter from what STATE I want to switch to another, the other STATES will not be derogated.

NOTE: This is not valid for variables, used and changed in more than one STATE!

 

The Grammar of E

The description of the grammar of "E" is following in Backus-Naur Form (BNF), notation of Unix-Tools "Bison". Words in lowercase letters are nonterminal symbols and words in uppercase letters are terminal symbols. The terminal symbols are returned from the lexer presented below. Note, the syntax may change in further releases of the matrix.

script		:
		|	load_part def_part event_blocks
		;


load_part	:	load_lines lib_lines
		;
		

load_lines	:	 
		|	load_lines  load_line	
		;


load_line	:	LOAD BEZEICHNER EQUAL BEZEICHNER AT_HOST 
			LEFT_ROUND STRING_C RIGHT_ROUND SEMICOLON
		;


lib_lines	:
		|	lib_lines lib_line
		;


lib_line	:	INCLUDE BEZEICHNER EQUAL BEZEICHNER 
			LEFT_ROUND STRING_C 
			RIGHT_ROUND SEMICOLON
		;


def_part	:	var_def_lines {g_symtbl = $1} func_def
		;


var_def_lines	:
		|	var_def_lines var_def_line
		;


var_def_line	:	variable_init SEMICOLON
		|	symbol_def SEMICOLON
		;


variable_init	:	symbol_def EQUAL constante
		;


func_def	:
		|	FUNCTION func_definition
		;


func_definition	:
		|	function_def func_definition
		;


function_def	:	function_head action_block
		;


function_head	:	symbol_def function_para var_def_lines
		;


function_para	:	LEFT_ROUND parameters_def 
			RIGHT_ROUND
		;


parameters_def	:	symbol_def
		|	symbol_def COLON parameters_def
		;


symbol_def	:	INT_T variable_def
		|	FLOAT_T variable_def
		|	STRING_T variable_def
		;


variable_def	:	BEZEICHNER
		|	BEZEICHNER index
		;


constante	:	INT_C
	   	|	FLOAT_C
	   	|	STRING_C
		|	CHAR_C
		;



event_blocks	:	event_block
		|	event_block event_blocks
		;
		


event_block	:	event action_line
		;

		

event		:	state EVENT BEZEICHNER evnt_para_block
		|	state EVENT BEZEICHNER 
			DOUBLEPOINT BEZEICHNER evnt_para_block
		;



state		:
		|	BEZEICHNER
		|	state OR BEZEICHNER
		;
				


evnt_para_block	:	LEFT_ROUND evnt_paras 
			RIGHT_ROUND
		;
		


evnt_paras	:	
		|	evnt_para
		|	evnt_para COLON evnt_paras
		;
		


evnt_para	:	expression
		|	GET variable
		;



action_block	:	LEFT_GESCH action_lines 
			RIGHT_GESCH
		;
		


action_lines	:	action_line
		|	action_line action_lines
		;



action_line	:	statement_line
		|	expression_line
		|	action_block
		;



statement_line	:	statement
		;



expression_line	:	expression SEMICOLON
		;
		


statement	:	if_statement
		|	for_statement
		|	while_statement
		|	ret_statement
		|	break_statement
		|	event_block
		|	state_statement
		;



expression	:	action
		|	queue_statement
		|	function
		|	compair
		|	zuweisung
		|	constante
		|	variable
		|	exit
		;



compair		:	expression EQUIV expression
		;
		


action		:	BEZEICHNER DOUBLEPOINT 
			BEZEICHNER para_block
		;



para_block	:	LEFT_ROUND paras RIGHT_ROUND
		;



paras		:	
		|	expression
		|	expression COLON paras
		;
		


if_statement	:	IF LEFT_ROUND expression 
			RIGHT_ROUND action_line %prec ELSE
		|	IF LEFT_ROUND expression 
			RIGHT_ROUND action_line ELSE action_line
		;
				

for_statement	:	FOR for_para action_line
		;



ret_statement	:	RETURN SEMICOLON
		|	RETURN expression SEMICOLON
		;



break_statement	:	BREAK SEMICOLON
		;



queue_statement	:	QUEUE_REL LEFT_ROUND expression 
			RIGHT_ROUND action_line
		|	QUEUE_ABS LEFT_ROUND expression 
			RIGHT_ROUND action_line
		|	QUEUE_REL_P LEFT_ROUND expression 
			RIGHT_ROUND action_line
		|	DEQUEUE LEFT_ROUND expression 
			RIGHT_ROUND 
		;


for_para	:	LEFT_ROUND expression SEMICOLON expression 
			SEMICOLON expression RIGHT_ROUND
		;
		

while_statement	:	WHILE LEFT_ROUND expression 
			RIGHT_ROUND action_line
		;


state_statement :	STATESWITCH LEFT_ROUND BEZEICHNER 
			RIGHT_ROUND SEMICOLON
		|	STATEPOP SEMICOLON
		|	STATEPUSH LEFT_ROUND BEZEICHNER 
			RIGHT_ROUND SEMICOLON
		;
		

zuweisung	:	variable EQUAL expression
		;



function	:	BEZEICHNER para_block
		;



exit		:	EXIT LEFT_ROUND expression RIGHT_ROUND
			{
				new_stmt = new_symb();
				new_stmt->whatami = IAM_EXIT;
				new_stmt->para = $3;
				$$ = new_stmt;
			}
		;	



variable	:	BEZEICHNER
		|	BEZEICHNER index
		;



index		:	LEFT_ECKIG expression RIGHT_ECKIG

 

The Lexical analysator of E

if						return (IF);
else						return (ELSE);
while						return (WHILE);
for						return (FOR);
int						return (INT_T);
float						return (FLOAT_T);
string						return (STRING_T);
return						return (RETURN);
break						return (BREAK);
functions					return (FUNCTION);
"->"						return (EVENT);
use						return (LOAD);
ALIAS						return (ALIAS);
WITH						return (WITH);
LIB						return (LIB);
include						return (INCLUDE);
state						return (STATESWITCH);
statepop					return (STATEPOP);
statepush					return (STATEPUSH);
exit						return (EXIT);
queue_rel					return (QUEUE_REL);
queue_rel_p					return(QUEUE_REL_P);	
dequeue						return (DEQUEUE);	
queue_abs					return (QUEUE_ABS);
@[a-zA-Z0-9_\-]*(\.[a-zA-Z0-9_\-]*)*		return (AT_HOST);
[a-zA-Z_][a-zA-Z0-9_]*				return (BEZEICHNER);
[+-]?[0-9]*					return (INT_C); 
[+-]?[0-9]*(\.[0-9]*)?([EeDd][+-]?[0-9]+)? 	return (FLOAT_C);
\"[^"]*\" 					return (STRING_C);
\'[^']*\' 					return (CHAR_C);
";"						return (SEMICOLON);
":"						return (DOUBLEPOINT);
","						return (COLON);
"="						return (EQUAL);
"=="						return (EQUIV);
"!="						return (EQUIV);
"<"						return (EQUIV);
">"						return (EQUIV);
">="						return (EQUIV);
"<="						return (EQUIV);
"("						return (LEFT_ROUND);
")"						return (RIGHT_ROUND)
"{"						return (LEFT_GESCH);
"}"						return (RIGHT_GESCH);
"["						return (LEFT_ECKIG);
"]"						return (RIGHT_ECKIG);
"^"						return (GET);
"|"						return (OR);
\n						myline++;
\t						;
" "						;
.						return (ANY);
^#.*$						;