Table des Matières

A small RPL scripting language

In every good program, a solid script language is provided. For example, in Office, Visual Basic For Application is used to allow easy programmation. And I needed a good language to automate the WinBSC program. So I started a small interpreter to analyse small arithmetic expressions, manage variables and so on.

The trend in scripting is to provide a ActiveX scripting language so that you can insert it in your project, if this one has a COM server. Your application must have a COM automation server so that the scripting language could access to program’s variable.

As the WinBSC program have no COM server, my server needn’t COM interface. So I did a interpreter which is quiet as complete as VBA but cannot access to COM object.

This interpreter has a syntax close to a “C” program. You can easily add the code in your program and add variables names that are comes from your program so that in the script namespace some variable of your program are visibles.

My philosophy of scripting

This scripting language is freely inspired by the C-language syntax and HP’s RPL in the way of manipulating variables :

id name description
0 VAREMPTY an uninitialised variable (internal use only)
1 VARINT a 32 bits signed integer
2 VARREAL a floating point number (it’s a “c” double)
3 VARSTRING a string of any size
4 VARLIST a list of any object
5 VARARRAY a n-dimension array of VARREAL

The id number is the value returned by the type() function. So you can test the type of a variable in a list, as you can put anything in it.

Documentation

Notations

In the description below, I will use these notations for specifiing types of variable

* any object
I VARINT : integer
R VARREAL : real
S VARSTRING : string
L VARLIST : list
A VARARRAY : array

Mathematical operators

Addition
+ (I/R, I/R) return a real until I and I
+ (*, L) add variable at the head of list
+ (L, *) add variable at the end of list
+ (I/R/S, S) add the string conversion of variable at the head of the string
+ (S, I/R/S) add the string conversion of variable at the end of the string
+ (I/R, A) add to every array elements the given number
+ (A, I/R) add to every array elements the given number
Substraction
- (I/R, I/R) return a real if an operand is real, otherwise int
- (I/R, A) return matrix with substraction
- (A, I/R) return matrix with substraction
- (A, A) if same size, substracts matrices
Multiplication
* (I/R, I/R) return a real if an operand is real, otherwise int
* (I/R, A) return matrix operand by elements multiplication
* (A, I/R) return matrix elements multiplication by operand
* (A, A) should multiply matrix, but not implemented yet
Division
/ (I/R, I/R) return a real if an operand is real, otherwise int
/ (I/R, A) return matrix operand by elements division
/ (A, I/R) return matrix elements by operand division
/ (A, A) no lu decomposition implemented yet
Power
^ (I/R, I/R) return a real if an operand is real, otherwise int

Logical operators

! (I) logical not
&& (I, I) logical and
|| (I, I) logical or
==(*, *) equal
!=(*, *) inequal
<=(I/R, I/R) lower or equal
<=(S, S) lower or equal
< (I/R, I/R) lower
< (S, S) lower
>=(I/R, I/R) greater or equal
>=(S, S) greater or equal
> (I/R, I/R) greater
> (S, S) greater

Others operators

The array element operator allows you to acceed directly to the elements of an array or a list. For an array, there should be as many operators as the dimension of the array. There are no check about the validity of a write in a matrix, so you could set a part of the matrix with vector of different size, so take care.

[](A, I) return the array pointed by this dim
[](L, I) return the object pointed by this index in the list

Build-in functions

Mathematic
I = mod(I, I) modulo
I = abs(I) absolute value
R = abs(R) absolute value
R = ceil(R) ceiling
R = floor(R) floor
R = sqr(I/R) squared value
R = sqrt(I/R) square root
R = exp(I/R) exponential
R = ln(I/R) neperien logarithm
R = log(I/R) base 10 logarithm
R = alog(I/R) base 10 exponential
R = sin(I/R) sinus
R = cos(I/R) cosinus
R = tan(I/R) tangente
R = asin(I/R) inverse sinus
R = acos(I/R) inverse cosinus
R = atan(I/R) inverse tangente
R = sinh(I/R) hyperbolic sinus
R = cosh(I/R) hyperbolic cosinus
R = tanh(I/R) hyperbolic tangente
R = fact(I/R, I/R) return factorial
Random and time
R = rand() return a number in [0..1[
I = time() return UTC time (nb of seconds since 1/1/1970)
Type conversion
I = type(*) return type of object (0..5)
I = int(I/R) convert to integer
I = int(S) convert to integer
R = real(I/R) convert to real
R = real(S) convert to real
String
S = str(*) standard convertion to string
S = str(I/R, S) use a formating string for string
conversion on I/R (like printf)
S = upper(S) convert string to uppercase
S = lower(S) convert string to lowercase
List
L = list() create an empty list
* = head(L) return first element of the list
L = tail(L) return list without first element
Array
A = array(d1, …) create a n-dim array
Multitypes
S = sub(S, I/R, I/R) return substring between the indices
L = sub(L, I/R, I/R) return sublist between the indices
A = sub(A, L, L) return submatrix between lists of indices
I = size(I/R) just return 1 as integer
I = size(S) length of string
I = size(L) number of elements in the list
L = size(A) return array dimensions

Predefined variables

These variables are assigned with default value at the beginning of execution of the script, but can be changed by user.

Constants
true (int) 1 : logical true
false (int) 0 : logical false
pi (double) 3.1415926535897932385
e (double) 2.7182818284590452354
Command line parameters
arg (list) The arg variable store the command line parameters you’ve given, in a list. The size of the list gives you the number of the parameters.

Control structures

All of the control structures are preceded by the #symbol to give the code more readability.

#if(test);

#else;

#end;
The standard if..then..else..end statement.
If the test is true then does the first bloc else does the second if present.
#do;

#until(test);
The do loop. It does the bloc until the test is true.
#while(test);

#end;
The while loop. While the test is true, it loops.
#exit(); The exit. When this operand is achieved, the script stops execution and leave.
#return(*); Returns a variable to the caller (see functions).

Functions

There are no function declaration in this style of script. But you can structurate your program in creating many files. Each files is a function. Then in a script, you can call another files with the call function, which works as descripted below.

var1 = call(“filename”, arguments …) This function execute the script “filename”
It transmits the arguments in the arg variable.
You can return variables by using the control structure #return(var2) which will store in the caller variable var1 the called variable var2.

With this behavior you can easily make big program. Recursion works also.

Examples

Example 1, classic chess queen problem

The script below solves a famous chess problem : the 8-queens problem. You use it in a command line windows. The interpreter is an executable called “script.exe”. So you execute a script this way :

script name_of_script { arguments }

The parameters in brackets are optionals.

So you use the script below called queens.rpl this way :

script queens.rpl 8

The 8 is the size of the chess board. The way we retrieve this argument is explained in the code.

/* Solving the queen problem on a n-by-n chess board
What's the problem :
When you have a n-by-n chess board, place n queens on it,
so that none of the queen can take another one.
With the dimension of the board, there are several solutions :
dim : 1 2 3 4  5 6  7  8   9  10   11    12 ...
sol : 1 0 0 2 10 4 10 92 352 724 2680 14200 ...
sec :                        524 
This program solves this problem with a backtracing algorithm.
*/
 
// retrieve arguments
// in arg variable is stored the list of arguments
// first check if there are some
#if(size(arg)==0);
  "usage reines.rpl size_of_board_as_int";
  #exit;
#end;
 
// check the argument is an integer
#if(type(arg[1])!=1);
  "usage reines.rpl size_of_board_as_int";
  #exit;
#end;
 
// size of the chess board
ordre = arg[1];
start = time();
ordre+" by "+ordre+" chess board solutions are :";
 
// private vars initialisation
sol = array(ordre); // sol is an array
fin = false;
nbs = 0;
i   = 0;
#while(!fin);
   i = i + 1;
   #while( (i<=ordre) && (!fin) );
      tst = false;
      #while( (sol[i]<ordre) && (!tst) );
         sol[i] = sol[i] + real(1);
	 // checking that queen j can't take preceding ones 
         tst = true;
         j = 1;
	 #while( (j<i) && (tst) );
	    #if( (sol[i]==sol[j]) ||  (abs(int(sol[i]-sol[j]))==(i-j)) );               
                tst = false;
	    #end;
	    j = j + 1;
         #end;
      #end;
      #if(!tst);
         sol[i] = 0;
         i = i - 1;
         #if(i==0);
            fin = true;
	 #else;
	    i = i - 1;
         #end;
      #end;
      i = i + 1;
   #end;
   #if(!fin);
      sol;
      nbs = nbs + 1;
      sol[ordre] = 0;
      i = ordre - 2;
   #end;
#end;
"There are "+nbs+" solutions";
"Time elapsed "+(time()-start)+" second(s)";

Example 2, pgcd calculus

Thi script illustrated the way of calling another script. In fact, it calls himself recursively. It is the euclidean algorithm for pgcd calculus and returns the pgcd of the given 2 numbers.

Use it with this command line

script pgcd.rpl number1 number2

The script

// calculating the pgcd of two numbers
// with the recursive euclidean algorithm
// check that there are two arguments
#if(size(arg)!=2);
  "bad number of arguments";
  #exit();
#end;
a = arg[1];
b = arg[2];

// check the type
#if( (type(a)!=1) && (type(b)!=1) );
  "bad type of arguments";
  #exit();
#end;

// the algorithm
#if(b==0);
  #return(a);
#else;
  #return(call("pgcd.rpl", b, mod(a, b)));
#end;

Benchmarks

I used this little script to bench my interpreter. It times a one million loop and the calling procedure. You can with these results appreciate the speed, although you can accerately times every functions.

// benchmarking of the interpreter
tstart = time();
i = 0;
#while(i<1000000);
  i = i + 1;
#end;
(time()-tstart)+" s";
tstart = time();
lst = list();
i = 0;
#while(i<1000);
  i = i + 1;
  a = int(1000000*rand());
  b = int(1000000*rand());
  lst = lst + { a b call("pgcd.rpl", a, b) };
#end;
(time()-tstart)+" s";

The results for a Pentium II 300 running NT40 Workstation + SP3 are :

Downloading

Version 1.02, The interpreter (executable and source code) and some examples of scripts : script102.zip