libfawk API overview

This is the high level API documentation of libfawk. The focus is on giving an overview on how the API works, what a host application programmer needs to do to run libfawk scripts. Low level details, such as how exactly each API function behaves and what arguments they take are documented in trunk/libfawk/*.h.

Script context

Libfawk has a reentrant API. The host application can create multiple, independent script contexts (referred to as context in this document). A context fully describes the virtual machine, the parser and all internal states of a script.

The host application creates the context using fawk_init() and destroys it using fawk_uninit(). After creation the context needs to be set up by the host application:

After this point the host application calls fawk_parse_fawk() (or the parser of any other supported language) to parse and compile a script. The parser runs a loop calling back the get_char() function of the context until it returns -1 (EOF) or a parsing error occurs.

Once the script is parsed, the host application typically calls main() of the script. Then the host application keeps one or more script contexts and call script functions upon events or requests.

All relevant calls for context management are declared in libfawk.h.

Cell

A cell (fawk_cell_t) is a type+value structure that holds a single value for the libfawk VM. Variables, return values, temporary values during expression evaluation are all stored in cells. Cells are allocated using malloc().

Debugging a cell is possible calling fawk_dump_cell(), which will print the value and optionally metadata of the cell to stdout.

The relevant types and calls are all declared in libfawk.h

Cells are allocated using one of the following calls:

After the allocation, the value of a cell is NIL.

Once the code has a fawk_cell_t *, it can be free'd using fawk_cell_free() or its value can be overwritten from another cell by fawk_cell_cpy(). Before using fawk_cell_cpy(), the destination cell shall be fawk_cell_free'd by the caller.

The relevant functions are declared in execute.h and symtab.h.

String cells

A libfawk string is one of the possible cell value types.

Strings are stored in fawk_str_t, cell type is FAWK_STR or FAWK_STRNUM. The struct gives the head of the string and is allocated to as large as needed for hosting the whole string. The allocation is normally done using malloc.

Libfawk strings are immutable and reference counted: if a string needs to be changed, it is copied and the copy is already created with the modification applied. Each time a cell starts using a string, the reference counter of the string is increased. On a cell copy, the reference counter is increased. On destroying a string cell, the string's reference counter is decreased. When the reference counter reaches 0, the string is free'd.

A new string can be created from a const char * string using the fawk_str_new_from_literal(). This is typically used when loading a new cell of type FAWK_STR. When a new string is created by changing an existing string, fawk_str_clone() is used to get a real copy with refco=1. When a virtual copy (reference counter increase) is needed, e.g. when copying a string into a new cell without modification, fawk_str_dup() should be called. fawk_str_free() is its pair, the virtual free that decreases the reference counter and frees the string if needed.

If a cell is FAWK_STRNUM, it stores both the original string and its numeric value so the code won't need to convert the string to number again.

All relevant calls are in str.h.

Array cells

A libfawk array is one of the possible cell value types.

A libfawk array is fawk_arr_t, cell type is FAWK_ARRAY. Libfawk arrays are always hash tables. The index of the table is fawk_arridx_t and the value for each table entry is a cell too.

A new array value can be created using fawk_array_init() and it can be destroyed using fawk_array_free(). Array members can be retrieved or created using fawk_array_resolve_c(). Array-in-array is supported.

Relevant types are declared in libfawk.h, functions in array.h.

Converting types

Calls fawk_cast_to_num() and fawk_cast_to_str() from cast.h can be used to convert a cell to a different type in place.

The stack

The stack in the context is used by the VM to evaluate expressions and to pass function arguments.

The host application can use the low level fawk_push_alloc() to push a new, NIL cell on the top of the stack (and then fill in the value and type) or the fawk_push_*() helper functions that do that automatically.

The top of the stack can be removed using fawk_pop(). The value of the cell is copied to dst, which needs to be free'd using fawk_cell_free(). It is also possible to look at cells addressed from the current top of the stack or from the bottom of the stack, without removing anything from the stack, using the fawk_peek() call.

The only place where host application needs to manipulate the stack is normally c->script function calls.

Relevant calls are declared in execute.h.

The VM

The libfawk virtual machine uses a stack of cells and a program memory where the precompiled instructions of the script are stored. The host application may prepare the stack and instruct the VM to call a script function. The script function call is always blocking the host application: it returns only if the script function returned or if the run limit is reached.

When calling a function the host application follows this sequence:

All relevant declarations are in execute.h.

Script callable C functions

The host application may register functions in the script context, typically after the context is created, using fawk_symtab_regcfunc(). When the script calls the function, the caller provided C callback function is called. The callback function is of type fawk_cfunc_t and gets:

The function implementation can use the macro FAWK_CFUNC_ARG() to access arguments from the stack. The function does not need to free arguments, the call mechanism will do that after the function has returned.

The function is free to load any value/type in the return value cell. The function may also read or write script states (e.g. global variables) or even initiate script function calls.

The macro is defined in execute.h.

Script global variables

The host application may create script accessible global variables after context initialization, using the fawk_symtab_regvar() call. At any time the host application can look up global variables, created by the script or by the host application earlier, using the fawk_sym_lookup() call. Function local variables are not accessible to the host application.

The relevant calls are in symtab.h

Host application glue layer and config

If the single-c-source version is used, before the #include happens, the host application shall configure the base types and API scope:

typedef double fawk_num_t;
typedef long fawk_refco_t;
#define FAWK_API static

If the host application wishes to disable the fawk_print() builtin (so the script won't write to stdout), it should define the following macro:

#define FAWK_DISABLE_FAWK_PRINT

In both the single-c-source and lib versions the host application needs to implement the public function libfawk_error() (see parser.h) that can handle the error messages generated by the parser or executer.

For parsing, the host application needs to fill in the .get_char function pointer of the context (pointing to a local implementation) and if includes are to be supported, the .inclode function pointer too. This needs to be done to each script context.

Memory allocation override

When using the single-c-source version, the host application may override memory allocation calls by defining the following macros before including the libfawk:

#define fawk_malloc(ctx,size) malloc(size)
#define fawk_calloc(ctx,n,m) calloc(n, m)
#define fawk_realloc(ctx,ptr,size) realloc(ptr, size)
#define fawk_free(ctx,ptr) free(ptr)

When an allocation function returns NULL, the script fails - the fatal error is marked in the script context and parsing/execution returns immediately.