Symisc UnQLite

An Embeddable NoSQL Database Engine

Star

Follow @symisc



Introduction to the unQLite Constant Expansion Mechanism.

The following quick guide is what you do to start experimenting with the Jx9 (Via UnQLite) constant expansion mechanism without having to do a lot of tedious reading.


A constant is an identifier (name) for a simple value. As the name suggests, that value cannot change during the execution of the script. A valid constant name starts with a letter or underscore, followed by any number of letters, numbers, or underscores. Constants name are case-sensitive. The constant expansion mechanism under UnQLite (Via Jx9) is extremely powerful yet simple and work as follows:


Each registered constant have a C procedure associated with it. This procedure is known as the constant expansion callback which is responsible of expanding the invoked constant to the desired value. For example the C procedure associated with the “__PI__” constant expands to 3.14 (the value of PI), the “__OS__” constant expands to the name of the host Operating Systems (Windows, Linux, etc.) and so on.


Constants and their associated callbacks are registered via a single interface: unqlite_create_constant(). All of the built-in constants (over 142) such as __JX9__, __DATE__, PATH_SEPARATOR and so forth are registered using exactly this interface.


We will start our tutorial by creating a simple constant named  __PI__. That is, when the __PI__ identifier is seen in the executed Jx9 script, its associated procedure (see below for the implementation) gets called by the underlying Jx9 virtual machine. This procedure is responsible of expanding the PI  identifier to it's value (3.14 in our case).

The constant expansion callback signature is as follows:


void (*xExpand)(unqlite_value *,void *)


That is, a procedure that takes two arguments. The first argument is a pointer to a unqlite_value that the procedure must fill with the desired value for example 3.14 (value of PI) is stored in this pointer. Use the following interfaces to populate the object with the desired value:


unqlite_value_int()

unqlite_value_int64()

unqlite_value_bool()

unqlite_value_null()

unqlite_value_double()

unqlite_value_string()

unqlite_value_string_format()

unqlite_value_resource()


The second and last argument is a copy of the fourth argument to the unqlite_create_constant() function which is forwarded verbatim by engine.

Continuing with our __PI__ constant, here is the C procedure associated with it:


void PI_Constant(

     unqlite_value *pValue, /* Store expanded value here */

      void *pUserData       /* User private data (unused in our case) */

  ){

    /* Expand the value of PI */

     unqlite_value_double(pValue,3.1415926535898);

}


As you can see, the PI_Constant() is a simple C procedure that expand the value of PI using the unqlite_value_double() interface.


Another more complex constant is the __TIME__ constant which expands the current local time, here is the C procedure associated with it:


#include <time.h>

void TIME_Constant(unqlite_value *pValue,void *pUserData)

{

   struct tm *pLocal;

   time_t tt;

   /* Get the current local time */

   time(&tt);

   pLocal = localtime(&tt);

   /* Expand the current time */

   unqlite_value_string_format(pValue,

"%02d:%02d:%02d",

                   pLocal->tm_hour,

                   pLocal->tm_min,

                   pLocal->tm_sec

              );

}


We obtain the current local time using the libc localtime() routine, then we expand a printf() like formatted string holding the current time using the unqlite_value_string_format() interface.


A final example with the __OS__ constant which expands the name of the host Operating System.


void OS_Constant(unqlite_value *pValue,void *pUserData)

{

  #ifdef __WINNT__

     unqlite_value_string(pValue,"Windows",-1 /*Compute input length automatically */);

  #else

    /* Assume UNIX */

    unqlite_value_string(pValue,"UNIX",-1 /*Compute input length automatically */);

  #endif /* __WINNT__ */

}

We use unqlite_value_string() to expand the name of the host OS. Of course a more serious implementation would call uname() rather than using #ifdef macros. This is what the real built-in JX9_OS constant does.

Test The Constant Expansion Mechanism

Now we have implemented our constants expansion procedures, it's time to test the expansion mechanism. For that we will create a simple Jx9 script that invokes each of the defined constants (__PI__, __TIME__ and __OS__) and displays their expanded values. Here is, the script:


print '__PI__ value: ' .. /* '..' is the concatenation operator  */ __PI__ .. JX9_EOL;
print
'__TIME__ value: ' .. __TIME__ .. JX9_EOL;
print
'__OS__ value: ' .. __OS__ .. JX9_EOL;


When running, you should see something like that:


__PI__ value: 3.1415926535898

__TIME__ value: 15:02:27

__OS__ value: UNIX


Now, the main program. Note that you can get a working version of this program here



  1. int rc;
  2. unqlite *pDb;
  3. unqlite_vm *pVm;

  4. // Open our database;
  5. rc = unqlite_open(&pDb,"test.db",UNQLITE_OPEN_CREATE);
  6. if( rc != UNQLITE_OK ){ return; }

  7. // Compile our Jx9 program
  8. rc = unqlite_compile(pDb,JX9_PROG,sizeof(JX9_PROG)-1,&pVm);
  9. if( rc != UNQLITE_OK ){
  10.    /* Extract error log here */
  11.    const char *zBuf;
  12.    int iLen;
  13.    unqlite_config(pDb,UNQLITE_CONFIG_JX9_ERR_LOG,&zBuf,&iLen);
  14.    if( iLen > 0 ){
  15.       puts(zBuf);
  16.    }
  17.    return;
  18.  }

  19. /* Now register our constants and their associated C procedure */

  20.  rc = unqlite_create_constant(pVm,"__PI__",PI_Constant,0);

  21.   if( rc != UNQLITE_OK ){

  22.     return;

  23.    }

  24.   rc = unqlite_create_constant(pVm,"__TIME__",TIME_Constant,0);

  25.   if( rc != UNQLITE_OK ){

  26.      return;

  27.   }

  28.   rc = unqlite_create_constant(pVm,"__OS__",OS_Constant,0);

  29.   if( rc != UNQLITE_OK ){

  30.   return;

  31.    }


  32. //Configure our VM...

  33. // Execute our Jx9 program
  34. rc = unqlite_vm_exec(pVm);
  35. if( rc != UNQLITE_OK ){ return; }

  36. // Release our VM
  37. unqlite_vm_release(pVm);

  38. //Auto-commit the transaction and close our handle
  39. unqlite_close(pDb);


We open our target database using a call to unqlite_open() on line 6. This is often the first UnQLite API call that an application makes and is a prerequisite in order to compile Jx9 code using one of the compile interfaces.


We compile our Jx9 test program on line 10 using the unqlite_compile() interface.


We register our defined constants (__PI__, __TIME__ and __OS__) and their associated C procedures (PI_Constant(), TIME_Constant() and OS_Constant()) respectively on line 23, 27 and 31 using the unqlite_create_constant() interface.


And finally we execute our Jx9 program on line 39 using a call to unqlite_vm_exec(). Your VM output should look like this:


__PI__ value: 3.1415926535898

__TIME__ value: 15:02:27

__OS__ value: UNIX


We close our database handle on line 46 using the unqlite_close() interface.

What to do next

As you can see, the constant expansion mechanism under UnQLite is extremely powerful yet simple and it involves only a single call to unqlite_create_constant().

The developer who understands this mechanism will have a good foundation on using the in-process extending UnQLite interfaces. Now it's time to learn the more complex foreign functions mechanism.

Other useful links

Check out the Introduction To The UnQLite C/C++ Interface for an introductory overview and roadmap to the dozens of UnQLite interface functions.

A separate document, The UnQLite C/C++ Interface, provides detailed specifications for all of the various C/C++ APIs for UnQLite. Once the reader understands the basic principles of operation for UnQLite, that document should be used as a reference guide.

Any questions, visit the Support Page for additional information.


Symisc Systems
Copyright © Symisc Systems