Daniel Becker Bighelini

SPEL API: BOP Interpretive C Language

Blog Post created by Daniel Becker Bighelini on Jun 13, 2016

Chapter 2

BOP Interpretive C Language



Table of Contents


Spel methods                                                                                                                                           

Wand methods                                                                                                                                         


Web access                                                                                                                                              

Maintenance scripts                                                                                                                               

C Features                                                                                                

Supported Features                                                                                                                                     

Unsupported Features                                                                                                                                 

Data types                                                                                                                                                     

Casting between data types                                                                                                                        

Data conversions                                                                                                                                         

Examples Examples                                                                                                                                    

Builtin functions and variables                                                        

Spel-Only Vararg functions and variables                                                                                               

String functions                                                                                                                                             

Control functions                                                                                                                                          

(For Wand methods see builtins for getval/setval _by_name)                                                           

Reply building functions (Spel only methods)                                                                                        

Msg send and get reply functions and variables                                                                                    

Misc functions                                                                                                                                               





BOP includes a "C" like interpretive language.  The language is used in several places in a BOP system: Dob methods, Wand methods, Events, Web access, Maintenance scripts.


The use of the language varies slightly with each usage in BOP as follows:

Spel methods


Dob methods are defined in two steps.  The first is to define the method in the Dob in a majic file, giving the method name and signature in a METHODS block.  The second is to create a method in a .spl file with a name of the form Dob::method (), much like C++.


Used in this context, the interpretive method has access to the attributes of the object it is called on, much like a C++ method has access to attributes of the "this" object.  These attributes can be accessed by name, or prepended with "this.", as in printf ("%s %s", last_mod_date, this.last_mod_date).  In addition, object variables which contain references to Dobs can be used to access the attributes of the Dob in the same way as "this".  If cust is an object variable containing a customer Dob reference, for example, the last name can be accessed as cust.last_name.  NOTE: Dob attributes which are SRELs can be followed using the '.' notation.


For example a method defined on a call request could acces the customers location name as customer.location.name or this.customer.location.name.


These methods are invoked by sending a message to the Dob.  They can call other spell methods directly as well as sending and receiving messages from other objects.  When finished, they build a reply message to return to the original sender of the message to the Dob.

Wand methods


Wand methods are defined in .wnd files, in GLOBALS, DISPOB, LIST or STYLE blocks.  Wand methods are either invoked as a result of some event, like a mouse click or data changing, are called from other Wand methods or as a result of a message sent to the "root" object of vbop.


Unlike Domsrvr methods, Wand methods have no "this" pointer, rather they have a location in the display tree, based on the DISPOB, LIST or STYLE they are defined in.  Any data in the display tree can be accessed by a Wand method by giving an unambiguous path to it.  A path is built by prepending the name of the attribute with enough ancestors, separated by '.' to uniquely identify it.  For example, cst.last_name or cst_detail.cst.last_name.  Name searching is from the DISPOB, LIST or STYLE the method is defined in.  NOTE: GLOBAL methods are defined in the "root" object.


As there is no "this" pointer, the method of accessing attributes or Dobs by treating object variables as if they were "this" pointers, described above for Dob methods is not available for Wand methods.


Wand methods can call other interpretive Wand methods as well as builtin Wand methods by giving an unambiguous path to the method.


Arguments are passed by name as (name1 := value1, name2 := value2)



Events are essentially Dob methods that are defined and called dynamically rather than being defined in .maj and .spl files.  They are entered from the user interface, without a name and formal arguments.  They have a "this" pointer as other Dob methods do, which will reference the Dob they are being executed against.  The event configuration defines the formal arguments to these methods and determines the arguments available to the method.  NOTE:  Object variables can be used as "this" pointers as in Dob methods.

Web access


The Web access engine uses interpretive code.  When a user accesses the main URL for the engine, a method main () is called as:


      void menu (int cid, object args, int restarted)


See Web access documentation for details.  As in Dob methods there is a "this" pointer, whose attributes are strings whose names and values are defined in the web.cfg config file. Treating object variables as "this" pointers works as described for Dob methods.

Maintenance scripts


Several maintenance scripts call interpretive methods, usually in .frg files. These scripts are run by bop_cmd. There is no "this" pointer, but object variables referencing Dobs can be used as "this" pointers as described for Dob methods.


The interpreter uses a language very much like "C".  In fact the language is so similar that it's easier to describe the differences than to fully describe the interpreter language.


C Features

Supported Features


      if ()

      for ()

      while ()

      do while ()

      switch ()

      enum {}

      all math operations


      #include "file"



      //                                                C++ style comments

      /*                                               C style comments, but they  nest*/

Function declarations

      int foo (int first, string second) {}         function declaration with explicit


      type foo(...) {                            function declaration with (...)

                //  argc                             count of arguments

              //   argv[ integer ]             the n't argument


Unsupported Features



pointers, & and * pointer operators (See string functions below)

#include <file>

scoping of automatic variables

single character constants 'c'        char cc;

Pass by reference

initialization  ie  int i = 7;            declaration with initialization

int foo (first, second)                    old argument style

      int first;

      string second;


Data types

int                                          32 bit integer

long                                     32 bit integer

float  or double                   (same)



string                                   C++ object

object                                  BOP object

Casting between data types

target type

supported for


all but object


all but object


all but object


all but object


all but duration and object


all but date and object






Data conversions

int, long, float, double -> int, long, float, double   

                                                                    original value - possibly truncated

int, long, float, double -> string                 produces number as a string

date -> int, long, float, double                   produces UNIX timestamp

date -> string                                              "MM/DD/YYYY hh::mm::ss"

duration -> int, long, float, double, string produces number of seconds

string -> int, long, float, double                 number or NULL if is not numeric

string -> date                                              produces date from       "MM/DD/YY[YY] hh:mm:ss"

                                                                    or UNIX timestamp

string -> duration                                       produces duration from number of

                                                                    seconds, "mmm:ss" or "hhh:mm:ss"

string -> object                                          

   produces well known object reference from "node|proc|obj|refcounted". node and/or proc can be '@' for local. Refcounted should be '0'. Ie:  "@|Domserver|top|0"

object -> string                                           string object address as above.


Examples Examples

int seven;

seven = (int) "7";


date then;

then = (date) "1/1/95 00:00:00"  // Midnight, 1/1/95

then = (date) 0; // 1/1/70


duration how_long;

how_long = (duration) 300;  // 5 minutes

how_long = (duration) "5:00"; // 5 minutes


object top;

top = (object) "@|Domserver|top|0";


Builtin functions and variables


Following C conventions for defining a funtion or variable, the type of the function or variable is provide in the syntax.

type function_name(function_arguments);

type variable_name;

type array_variable_name[ ];


Spel-Only Vararg functions and variables


int arg_error (); 

Args came in a BPMessage - this fetches the error from the original message.


string class ();   

class name passed in from domrvr


string event ();   

event name passed in from domsrvr


int argc;   

Actual argument count


?? argv[];                            

Get nth argument up to length



String functions


'+' and '+=' operators // string concatenation

By example:

           string a;

           a = "fred";

           a = a + " is nuts";

           a += " is nuts";

int strlen (string target)

Returns the length of a string.

           len = strlen ("Hello world");  // len = 11


int sindex (string target, string regex [, int start])

Returns the start of regex pattern in the string relative to 0, or -1 if the pattern is not found. If start is specified the string will be examined only after start.

      i = sindex ("Hello world", "l"); // i = 2

      i = sindex ("Hello world", "l", 6); // i = 9


string substr (string target, string regex)

Returns the matching part of the string.

            bar = substr ("Hello world", " .*l");  // bar = " worl"


string substr (string target, int start [, int length])

returns the matching part of the string.  If length is omitted the rest of the string after start is returned.  NOTE: start is 0 relative, ie the first character is 0, not 1.

bar = substr ("Hello world", 3);  // bar = "lo world"

bar = substr ("Hello world", 3, 4);  // bar = "lo w"


string gsub (string target, string regex, string repl)

Return a string which was had every occurrence of "regex" replaced with "repl".


int split (string dest [], string target, string regex)

Splits the string as delimited by the regex and puts the pieces into dest (up to the size of dest).  It returns the number of pieces split from the original string.

      string tokens [3];

      split (tokens, foo, " ");

      // tokens [0] = "hello" tokens [1] = "world"


int extract(string dest[], string target, string regex)

Puts pieces of the string which match marked parts of the regex into dest (up to the size of dest).  It returns the number of pieces.

      string tokens [3];

      extract (tokens, foo, "H\\(.*\\)o[ ]+\\(.\\).*\\(.\\)$ ");

      // tokens [0] = "el" tokens [1] = "w" tokens [2] = "d"


string downcase (string source)

Returns the lowercase of a string.


tmp = downcase ("Hello world");  // tmp = "hello world"


string upcase (string source)

Returns the uppercase of a string.


string = upcase ("Hello world");  // tmp = "HELLO WORLD"


void printf (string format, ...)

Works like the "C" version.


string format (string format, ...)

Just like printf but returns the string rather than printing it.


bar = format ("I have %d cats", 27);  // bar = "I have 27 cats"


void sprintf (string result, string format, ...)

Works like the "C" version.


void sscanf (string target, string format, ...)

Works like the "C" version.


void logf (level, string format, ...)

Conditionally write to the log.


Format and ... are as in printf.


string expand (string format)

Expand variable references in a string.  This is similar to '$' expansion in a shell.  Words in the string to be expanded start with a tag character and are optionally surrounded by {}.  For example $NX_ROOT or ${NX_ROOT}.


Five kinds of expansion are possible:

  $.... are expanded exactly as shell environment variables.

  @.... are expanded as attributes of the "this" pointer (or display tree data for Wand methods).

  &.... are treated as object indirections or translations.  Some key attribute of an object is specified and replaced with the value of some other attribute of the object.

            Syntax:            &{val = factory.key->attr} 

The value "val" will be used as a key to identify a Dob of the type specified by "factory".

            If a Dob is uniquely identified, the attribute "attr" will  be fetched and its value will be inserted into the string.

            The value "val" can be a number or a string.  If it begins with a digit and is not quoted, it will be treated as a  number. Otherwise, it will be treated as a string.  "Val"   strings containg spaces must be quoted.

<....      are treated as a reference to a UNIX file, and replaced with the contents of the file.  The file name must be surrounded by braces.  References to environment  variables within braces are not expanded; if a file name includes such references, you must invoke the expand function twice.  See below for an example of this.


Single and double quotes are treated as in the shell.  ie. expansions happen within double quotes but not within single quotes.  For example:

      "nx root = $NX_ROOT,\n\

      description = @description,\n\

      contact 7 = &{7 = cnt.id->combo_name}"

      file_text = expand("<{" + expand("$HOME/file") + "}")

will expand to

      "nx root = /opt/LGNTpdm

      description = Once upon a time

      contact 7 = Picklemeier, Fred"

      file_text = "the contents of the file"


Control functions


string arch ()

Returns a string representing the architecture this is running on.  The current values are      

      "unix"  // Both server and client

      "win"   // Client only

For example: if (arch () == "win") ... else ...


int isserver ()

Returns a TRUE for server else false.


int isclient ()

Returns a TRUE for server else false.


void set_ilimit (int new_limit) (Spel methods Only)


The interpreter has a maximum instruction limit in order to avoid infinite loops.  After executing the maximum number of instructions without completing the interpreter returns a timeout error.  the limit defaults to 100000 but can be reset inside a function to any value up to 2^31 by calling set_ilimit (int).


NOTE:  Each call to set_ilimit () restarts the counter.  For example:

           set_ilimit (10000000);


??? call (string func, ...)  (Non Wand methods)

??? call (string func) (...) (Wand methods)

There is a builtin function call (string func, ...) which allows something like pointer-to-function.  It takes its first argument to be the name of a function to call and passes the rest of its arguments to the called function.  Since the function name can be a variable it effectively implements pointer-to-function.


The data type returned is whatever is returned from the called function. For example the following all produce the same result.

Non wand:

                       result = fred (1, 2, 3);

                       result = call ("fred", 1, 2, 3);

                       string name;

                       name = "fred";

                       result = call (name, 1, 2, 3);


                       result = fred (1, 2, 3);

                       result = call ("fred") (1, 2, 3);

                       string name;

                       name = "fred";

                       result = call (name) (1, 2, 3);


is_null (???? data)

Returns true if data is NULL, false otherwise.


is_empty (???? data)

Returns true if data is NULL or a blank string, false otherwise.


date now ()

Returns the current time as a date data type.


void sleep (int seconds)

I added this for testing, but it may be useful later.


int fork ()

Create a new thread.  Fork () returns TRUE to the original thread and FALSE to the new thread.  There is no parent/child relationship between the threads. The new thread can call methods or do any other work.  However, the new thread can not return from the method in which fork () was called.


void exec (string command)

Run the command as a background task.


int access (string filename, string mode)

Check that a file can be accessed. 


Filename is the name or path to a file.

Mode is a string consisting of any combination of the characters "rwxfp" that set the modes being tested.

           r = read                       w = write        x = execute     f = existance

           p = search PATH

access () returns TRUE (1) if the file can be accessed in the way(s) specified or FALSE (0) if not.


string getenv (string name)

Get the value of an environment variable


void setenv (string name, string value)

Set the value of an environment variable


(For Wand methods see builtins for getval/setval _by_name)


NOTE: See "builtins" for Wand method usage.


??? getval_by_name (string name);

Get the named attribute from dob corresponding to the "this" pointer. If the name of the attribute is known at compile time the attribute can simply be assigned from.  Getval_by_name allows the name to be calculated later. For example:

           string foo;

           string name;


           // In each case foo gets the same value.

           foo = customer.last_name;

           foo = getval_by_name ("customer.last_name");

           name = "customer.last_name";

           foo = getval_by_name (name);

??? setval_by_name (string name, ??? data);

Set the named attribute from dob corresponding to the "this" pointer. If the name of the attribute is known at compile time the attribute can simply be assigned to.  Setval_by_name allows the name to be calculated later. For example:

           Setval_by_name () returns its second argument.

           string name;

           // In each case foo gets the same value.

           customer.last_name = "fred";

           setval_by_name ("customer.last_name", "fred");

           name = "customer.last_name";

           setval_by_name (name, "fred");


string add_time_animator(string name, date start,

        duration interval, string pers_id,

        string method, string type, string user_smag);

                        (Spell methods only)

Add a time animator to the domsrvr.

Name is an arbitrary name used to find the animator later.

Start is the [first] time the animator should fire.

Interval is optionally the repat interval for the animatior.

Pers_id identifies the object (DOB) to be animated.

Method is the method to call on the object at the specified time.

Type identifies the type of object - must be "DOB" currently.

User_smag will be passed to the animated object's method.


string delete_animator (string pers_id, string method,

             string name);

(Spel methods only)

Delete (deactivate) the specified animator, uniquely identified by pers_id, method and name.  A "*" can be passed for one or more parameters to delete a set of animators. For example, all animators against an object about to be deleted, or all SLA animators against an object.


Reply building functions (Spel only methods)


void set_error (int error);

void clear_return_data (void);

void set_return_data ([int index,] data)

(Non Wand methods only)

There are three builtin functions to build a reply message to the caller. They are set_error (int), clear_return_data (void) and set_return_data (...).  Set_error () is used to set or reset the error flag in the return message. Clear_return_data () is used to clear any return message args previously set with set_return_data (). 


Set_return_data can be used in two ways - set_return_data (data) appends the data which can be any type to the end of the message and set_return_data (int index, data) which places the data at the index in the message. For example:


                       // NOTE:  E_INVALID is an enum

                       set_error (E_INVALID);

                       set_return_data ("Illegal operation requested");

                       set_return_data ("\"Fred\" is not a valid main course");




                       // NOTE:  E_INVALID is an enum

                       set_error (E_INVALID);

                       set_return_data (0, "Illegal operation requested");

                       set_return_data (1, "\"Fred\" is not a valid main course");


NOTE:  set_error () and set_return_data () apply to the function

called by sending a messaged rather than to functions called from pcode. This code is legal for example:

void set_my_special_error_code (int my_code)


           switch (my_error_code)


           case 1:  set_error (E_INVALID_CALL);  break;

           case 2:  set_error (E_UNKNOWN_NAME);  break;



void TT::bar ()



           set_my_special_error_code (1);



Msg send and get reply functions and variables


void send (object target, string method, ...)


int send_wait (int timeout, object target,

     string method, ...)


void send_catch (object target, string method,

     string reply_method,


void sendf (object target, string method, ...)


int sendf_wait (int timeout, object target,

    string method, ...)


void sendf_catch (object target, string method,

     string reply_method,...)


send () sends a message asynchronously.  Any arguments after the method argument are stuffed into the message and sent as data.


send_wait () sends a message and waits either for the (first) reply or a timeout.  Arguments are as in send ().  send_wait () return 0 if a reply was received or != 0 if a timeout occurred. If a reply is received msg, msg_length () and msg_error () will be set to correspond to the reply and can be used to access it until the next send_wait ().


send_catch () sends a message and specifies an interpretive method to be called on the response.


For example:

           // target could be passed in - the "this" pointer for

           // example, or generated for wellknown object

           object target;

           target = (object) "@|@|foo|0" ;

           send (target, "do-something", arg1, arg2, arg3);

           int timed_out;

           timed_out = send_wait (30, object_passed_in, "do-something",

                                   arg1, arg2, arg3);

           if (timed_out)                        { printf ("Message timed out\n");

           } else if (msg_error ())          {

               printf ("Error message with error %d and string \"%s\"\n",

                       msg_error (), msg [0]);

           } else {                       // Process the reply message



sendf (), sendf_wait () and sendf_catch () are similar to send (), send_wait () and send_catch () except that the arguments to the calling method are forwarded on by tacking them to the end of the parameters passed to send. For example:

           bar (int one, int two, int three)


           send (target, "fred", int four);


will send the message "four, one, two, three" to the target.  Or, for a more practical example:


           void send_to_cr_factory (string method, ...)


           sendf ((object) "@|domsrvr|TOP|0", "call_attr", "cr");



will send to the specified method on the call request factory with the args passed to it.


int msg_error ();

int msg_length ();

??? msg [];

There is one builtin variable and two builtin functions for examining response messages from sending to other objects.

"Msg"             is an array variable which is the contents of the last reply.

int msg_length () returns the length of the reply message.

int msg_error () returns the error code from the reply message.  NOTE:

For example:

if (msg_error () == 0)           {

    int i;

        for (i = 0; i < msg_length (); ++i)        {

                set_return_data (i, msg [i]);




Misc functions


duration workshift_abs2work (string sched, date start,

     date end);

Calculate amount of worktime as specified by schedule that occurrs between the start and end date - time spec.  For example: if the schedule specifies 8:00am - 5:00pm, start is 11/11/95 4:30pm and end is 11/12/95 10:00am, calculated work time is 2:30:00.


date workshift_work2abs (string sched, date start,

     duration length);

Given a start time, a work schedule and a worktime, calculate the actual end date.


For example: if the schedule specifies 8:00am - 5:00pm, start is 11/11/95 4:30pm and length is 2:30:00 calculated finish time is 11/12/95 10:00am.





                              Business Objects Paradigm - Programmer's Reference                                          r2–1