Application Programming Interface of Z-Script

(API for version 1.3 is here)

As demonstrated in main.cpp, the following API functions are for building an executable program:

void* api_parse_file(const char* fname, char *error)
      -- Parses script in the file and returns a module or NULL in case of error.
         The second variable must point to memory (>= 256 bytes) for error message.

int api_exec_module(void *module, char *error)
      -- Executes the module and return a negative integer if failed.
         The second variable must point to memory (>= 256 bytes) for error message.

void api_delete_module(void *module)
      -- Delete the module and release resources allocated by the module.

void api_add_arg_ikey(int key, const char* value)
void api_add_arg_skey(const char* key, const char* value)
      -- adds the key-value to be extracted by the build-in getarg(key) function.

void api_add_path(const char *path)
      -- adds path for searching modules.

In applications that need to evaluate script code in a string, as in embedding script in HTML document, a module may be created directly from the string and objects may be passed from one module to another dynamically using these functions:

void* api_parse_string(void *string, char *error)
      -- Parses script in the string and returns a module or NULL in case of error.

void api_set_module_object(void *module, const char *name, void *ptr, int type)
      -- Sets a user object to the module.

void* api_get_module_object(void *module, const char *name)
      -- Gets a  module object by name.

A module manages memoy of its own and imported modules and may be executed many times. But in a thread-safe application, each thread should parse script files independently.

You may build dynamic link libraries (DLL) of primitive functions by including api.h (and scan.h if you want to redefine operators for you objects) in your C or C++ source code and linking the library to zs.lib. A primitive function must have the prototype of

void *func(void *caller, int nargs, **args);

Where the caller is the expression in a scripting program that calls the function with given number (nargs) of arguments (args). A primitive function may be registered to Z-Script by calling the function

void api_add_primitive(const char* name, int type, void* func)

You should assign a distinguished type ID to your object. Z-Script uses the ID to prevent name clash of functions. That is that the registered name of a primitive function will be formed with the type ID attached to the given name. And when the primitive is called as

obj.func(...);

Z-Script will look for the function with the name of func+ID, e.g. func4835. A primitive function may return NULL or an object create by one of the API functions:

void* api_create_null(caller)
      -- creates a null object. 

void* api_create_integer(caller, int value)
      -- creates a integer type object. 

void* api_create_real(caller, double value)
      -- creates a real type object. 

void* api_create_string(caller, const char* str)
      -- creates a string type object. 

void* api_create_array(caller, size)
      -- creates a array type object with the given hash table size. 

void* api_create_user(caller, void* ptr, void* opfunc, void* destroy, int type)
      -- creates a user type object. 

The opfunc pointer in api_create_user() may be NULL or the address of a primitive function that will be called when a user object is an operand of a operator. The first argument passed to opfunc will be the user object, the second will be the operator ID (ref. scan.h), and the third will be null for unary operators or another object for binary operators. The destroy function pointer may point to a function of the prototype

void destroy(void *ptr)

that will be called when Z-Script is about to delete the user object.

In a primitive function, you may use these functions to query the object type of an argument:

bool api_is_null(void *object)
     -- returns true is the object is null; returns false otherwise.

bool api_is_integer(void* object) 
     -- returns true is the object is integer; returns false otherwise.

bool api_is_real(void* object)
     -- returns true is the object is real (double); returns false otherwise.

bool api_is_number(void *object)
     -- returns true is the object is integer or real; returns false otherwise.

bool api_is_string(void *object)
     -- returns true is the object is string; returns false otherwise.

bool api_is_array(void *object) 
     -- returns true is the object is array; returns false otherwise.

bool api_is_user(void *object)
     -- returns true is the object is user type; returns false otherwise.

int  api_get_type(void *object)
     -- returns the object type ID.

If you are expecting a certain type of object, you may just use the following functions to extract object values. They will throw an error when the object type is not what your are asking for.

int api_get_integer(void *caller, void* object)
     -- asserts that the object is integer and returns its value.

double api_get_real(void *caller, void* object)
     -- asserts that the object is real and returns its value.

double api_get_number(void *caller, void* object)
     -- asserts that the object is integer or real and returns its value.

const char* api_get_string(void* caller, void* object)
     -- asserts that the object is string and returns the pointer to the string.

void* api_get_ptr(void *caller, void *user)
     -- asserts that the object is user type and return the pointer
        that is passed to api_create_user function.

int  api_get_array_size(void *object)
     -- returns the size of the array object.

void* api_get_array_object(void *caller, void* array, const char* key)
void* api_get_array_object2(void *caller, void* array, int key)
     -- asserts that the array type is correct
        and return the object corresponding to the key.

void* api_peek_array_object(void* array, const char* key)
void* api_peek_array_object2(void* array, int key)
     -- tries to get the object corresponding to the key from the array
        and returns null if failed.

void api_set_array_object(void *caller, void* array, const char* key, void* value)
void api_set_array_object2(void *caller, void* array, int key, void* value)
     -- asserts that the array type is correct, removes the key/value first,
        and then sets the key/value in the array.

Calling script function from your primitive functions may be achieved through these functions:

void* api_get_func(void *caller, const char* name)
     -- returns the pointer to the named script function;
        throw error is the function is not declared in script.

void* api_call_func(void *func, int nargs, void** args)
     -- call script function with arguments

And finally, you should let the caller to handle critical error using this function:

void api_runtime_error(const void *caller, const char* msg)

For a user object, if a primitive function is registered with the name "__get ", it will be called in an access expression, i.e., u.member or u[a, b, c...]. In the former case, the function will be get tow arguments with the first being the user object and the second being a string object whose value is "member"; and in the latter, the number of arguments and their types depend on expressions inside [], but the first argument is the user object. Similarly, if a primitive function is registered with the name "__set ", it will be called in an assignment expression, i.e., u.member = expr or u[a, b, c...] = expr and the last argument is the object to be assigned to u. And if a primitive function is registered with the name "__copy ", it will be called in an assignment expression, e.g., user2 = user1, and will get user1 as the only argument; otherwise user2 will be an alias of user1. The following C++ code demonstrates how to define primitive functions for a simple 3D point object.

#include "api.h"
#include 
#include 

#define P3D_TYPE		'P3D'

///////////////////////////////////////////////////////
// 3D point class
//
class zsP3D
{
public:
	zsP3D() : x(0), y(0), z(0) { }
	~zsP3D() { }
	double x, y, z;
};

///////////////////////////////////////////////////////
// Class for registering primitive function
//
class zsRegPrimitive
{
public:
	zsRegPrimitive(const char* name, int type, void* func) { api_add_primitive(name, type, func); }
};

///////////////////////////////////////////////////////
// Report argument input error
//
void input_error(void *caller) { api_runtime_error(caller, "bad input"); }

///////////////////////////////////////////////////////
// This will be called when reference counts to
// a zsP3D object are reduced to zero.
//
void p3d_destroy(void* ptr) { delete (zsP3D*)ptr; }

///////////////////////////////////////////////////////
// Define + operator for zsP3D 
//
void* p3d_opfunc(void *caller, int nargs, void** args)
{
	if (nargs != 3) input_error(caller);
	zsP3D *l = (zsP3D*)api_get_user(caller, args[0], P3D_TYPE);
	int op = api_get_integer(caller, args[1]);
	switch (op) {
	case '+':
		if (api_is_number(args[2])) {
			// p3d + number
			real_t v = api_get_number(caller, args[2]);
			zsP3D *o = new zsP3D;
			o->x = l->x + v;
			o->y = l->x + v;
			o->z = l->x + v;
			return api_create_user(caller, o, p3d_opfunc, p3d_destroy, P3D_TYPE);
		}
		else {
			// p3d + p3d
			zsP3D *r = (zsP3D*)api_get_user(caller, args[2], P3D_TYPE);
			zsP3D *o = new zsP3D;
			o->x = l->x + r->x;
			o->y = l->x + r->y;
			o->z = l->x + r->z;
			return api_create_user(caller, o, p3d_opfunc, p3d_destroy, P3D_TYPE);
		}
		break;
		// more operator definitions here...
	}
	api_runtime_error(caller, "undefined operation for P3D object");
	return 0;
}


///////////////////////////////////////////////////////
// Create a zsP3D object. This may be called as
// a = p3p();
// a = p3p(0.1);
// a = p3p(0.1, 0.2);
// a = p3p(0.1, 0.2, 3);
//
void* p3d_create(void *caller, int nargs, void** args)
{
	zsP3D *o = new zsP3D;
	if (nargs > 0) o->x = api_get_number(caller, args[0]);
	if (nargs > 1) o->y = api_get_number(caller, args[1]);
	if (nargs > 2) o->z = api_get_number(caller, args[2]);
	return api_create_user(caller, o, p3d_opfunc, p3d_destroy, P3D_TYPE);
}
static zsRegPrimitive p3d0("p3d", 0, p3d_create);


///////////////////////////////////////////////////////
// Define ___get to be called by such a expression as
// a = p.name;
// a = p[idx];
//
void* p3d_get(void *caller, int nargs, void** args)
{
	if (nargs < 2) input_error(caller);
	zsP3D *o = (zsP3D*)api_get_user(caller, args[0], P3D_TYPE);
	if (api_is_string(args[1])) {
		// p.x
		const char *name = api_get_string(caller, args[1]);
		if (strcmp(name, "x") == 0) return api_create_real(caller, o->x);
		if (strcmp(name, "y") == 0) return api_create_real(caller, o->y);
		if (strcmp(name, "z") == 0) return api_create_real(caller, o->z);
		api_runtime_error(caller, "bad P3D member access");
	}
	// p[idx]
	int idx = api_get_integer(caller, args[1]);
	if (idx == 0) return api_create_real(caller, o->x);
	if (idx == 1) return api_create_real(caller, o->y);
	if (idx == 2) return api_create_real(caller, o->z);
	api_runtime_error(caller, "bad P3D member access");
	return 0;
}
static zsRegPrimitive p3d1("__get", P3D_TYPE, p3d_get);

///////////////////////////////////////////////////////
// Define ___set to be called by such a expression as
// p.name = v;
// p[idx] = v;
//
void* p3d_set(void *caller, int nargs, void** args)
{
	if (nargs < 3) input_error(caller);
	zsP3D *o = (zsP3D*)api_get_user(caller, args[0], P3D_TYPE);
	real_t v = api_get_number(caller, args[2]);
	if (api_is_string(args[1])) {
		// p.name = v
		const char *name = api_get_string(caller, args[1]);
		if      (strcmp(name, "x") == 0) o->x = v;
		else if (strcmp(name, "y") == 0) o->y = v;
		else if (strcmp(name, "z") == 0) o->z = v;
		else api_runtime_error(caller, "bad P3D member assignment");
		return 0;
	}
	// p[idx] = v
	int idx = api_get_integer(caller, args[1]);
	if      (idx == 0) o->x = v;
	else if (idx == 1) o->y = v;
	else if (idx == 2) o->z = v;
	else api_runtime_error(caller, "bad P3D member assignment2");
	return 0;
}
static zsRegPrimitive p3d2("__set", P3D_TYPE, p3d_set);

///////////////////////////////////////////////////////
// Define ___copy to be called by such a expression as
// p2 = p1;
//
void* p3d_copy(void *caller, int nargs, void** args)
{
	if (nargs < 1) input_error(caller);
	zsP3D *o = (zsP3D*)api_get_user(caller, args[0], P3D_TYPE);
	zsP3D *ret = new zsP3D;
	ret->x = o->x;
	ret->y = o->y;
	ret->z = o->z;
	return api_create_user(caller, ret, p3d_opfunc, p3d_destroy, P3D_TYPE);
}
static zsRegPrimitive p3d3("__copy", P3D_TYPE, p3d_copy);

///////////////////////////////////////////////////////
// Display class members
//
void* p3d_print(void *caller, int nargs, void** args)
{
	if (nargs < 1) input_error(caller);
	zsP3D *o = (zsP3D*)api_get_user(caller, args[0], P3D_TYPE);
	printf("x=%f, y=%f, z=%f\n", o->x, o->y, o->z);
	return 0;
}
static zsRegPrimitive p3d4("print", P3D_TYPE, p3d_print);

Alfter linked with Z-Script library, you can manipulate  a 3D point object as shown in the following script code

// load primitive functions in the library
load("point3d.dll");

// create 3D point object
p1 = p3d(1, 2, 3);

// show its members
p1.print();

// copy by assignment
p2 = p1;

// set members
p2.x = 10;
p2[1] = 20;

p2.print();

// get members of p1 and show them using buildin csv function
csv(p1.x, p1.y, p1[2]);

// + operator
p1 = p1 + 0.1;
p1.print();
p1 = p1 + p2;
p1.print();