Simple File Format Scripting

The libgfx library provides a very simple scripting facility which is primarily intended to support parsing of input files and control scripts. To use this package, you must include the header file:

    #include <gfx/script.h>

For more information about this package, you may want to examine the sample scripted application.

Processing Scripts

There are fundamentally two methods for processing scripts. First, you can pass a script to the system line by line using the function:

    int script_do_string(const char *line);
Alternatively, you can request that the script system process an entire file by calling:
    int script_do_file(const char *filename);

File Structure

Scripts processed by this package are assumed to be composed of a sequence of lines. Each line is processed separately, in sequence, and falls into one of the following categories:

  1. Lines containing only whitespace are ignored
  2. Lines whose first non-whitespace character is a '#' (hash mark) are treated as comments and are ignored
  3. All other lines are assumed to be a sequence of whitespace-separated tokens where the first token is a command name and subsequent tokens are arguments to this command.

Defining New Commands

The primary task of the scripting system is to map command name tokens into command procedures. These procedures, also referred to as "handlers", are responsible for actually performing the computation associated with a particular command. Handlers should conform to the following type definition:

    typedef int (*script_handler)(const char *name, char *argline);
The name argument is the command name token which caused this handler to be invoked. The argline argument is the remaining part of the script line following the command name token. Thus each handler is responsible for parsing its own arguments, to facilitate syntactic flexibility. However, the scripting package does provide several convenience routines to make this parsing fairly simple.

This package aims to allow separate code modules to register their scripting procedures without the main program having to call intialization functions for each one. To accomplish this by taking advantage of C++, and using constructors for global "variables" to perform this initialization implicitly. For every code module which contains scripting commands, you can append a block like the following example

    static script_defn handlers[] = {
	{"sample", sample_proc},
	{"other", other_proc},
    };

    REGISTER_COMMANDS(handlers);

Writing Command Handlers

A command procedure is declared as follows:

    int proc(const char *name, char *argline, Scripting *);
The argline is the remaining (uninterpreted) part of the command line following the command name. It is up to the handler to parse this line in whatever way it likes. However, the scripting system generally assumes that command will be given whitespace-separated token lists. Consequently, it provides some functions which make it easier to break apart such lines.

First, you can easily trim whitespace from the left and right ends of the string by calling one of:

    char *string_trimleft(char *line);
    char *string_trimright(char *line);
Each function returns a pointer to the string with the appropriate whitespace removed. To remove the first whitespace-separated token from the line, you can call
    char *string_shift_token(char **line);
This returns a pointer to the token and modifies the line pointer to point to the remainder of the original line.

Since this scripting package is primarily designed to support input of graphics scene descriptions, it is common to need to parse lists of numbers. The scripting framework provides functions to do this automatically:

    int string_shift_numbers(char **line, double *x, int n);
    int string_shift_numbers(char **line, float *x, int n);
    int string_shift_numbers(char **line, int *x, int n);
The n variable is the maximum number of elements that can be read into the array x. The actual number of elements read are returned from the function and the line pointer is modifed to point to the remainder of the line. See the accompanying scripting example for more details.