Structure of the API
Use of DbgLog
DbgLog filters and logs debugging/error messages according to errno, calling module/function, warning keyword and debugging level. Particular errno/keyword combinations can be made fatal. Output log files depend on keyword.
To use DbgLog, simply #include <DbgLog.h>.
Error reporting can be done using:
void DbgLog(const char * mod, const char * key, unsigned char lev, const char * mes, ...) ;
'mod' is the calling module or function name. This is the main context
data, as this information will structure the actual hierarchy of calls.
'key' is a debugging key, an identifier tag that will direct logging and
filtering of this message. 'lev' is a numeric indication of severity
(the lower, the more severe, with 0 having sense as "system-level error").
DbgLog("MyFunction","dummyerror",16,"This is my error number %d",num++) ;
DbgLog_UpDbgLvl and DbgLog_DnDbgLvl respectively toggle on and off displaying messages from the error levels given by the set bits of the mask (1 being the level 0, 1<<1 the level 1, and then on until 1<<31).
Error stack and recursive calls
DbgLog_OpenBlock and DbgLog_CloseBlock are used to open and close trapping blocks, that is to suspend error reporting. No messages will be printed, except when DbgLog_Flush is called, that will print all trapped error entries from the last call to DbgLog_OpenBlock.
Managing imbricated failures
When using imbricated calls to functions using DbgLog, the uppermost can fail because one lower call failed. In this case, using DbgLog again would result in two messages for the same material error.. While this may be desirable, most often this would only be confusing at debug time. It is advisable to limit the use of DbgLog on generation of error, that is: a failed system call (so the errno variable is set to a meaningfull value), a check that showed some discrepancy in data, wrong arguments...
Two complex structures are used to store objects during execution. These are the XCLE_Stack and XCLE_Hash objects, defined in <XCLE/Containers.h>.
The XCLE_Stack object holds arguments and results while executing programs. It can be manipulated by poping and pushing data (with XCLE_StackPop and XCLE_StackPush respectiveley) on the lowermost stack level, thus lowering/raising other objects already on the stack. An object on a given level can be accessed through XCLE_StackGet, that returns the object without removing it from the stack.
The XCLE_Hash object can be used as a variables table. It holds (name,object) pairs, giving access to the objects by their name in an efficient way. XCLE_HashSet, XCLE_HashGet and XCLE_HashDel provide ways to manipulate this association, by respectively creating/modifing an entry, retriving the contents of an entry, or deleting an entry.
XCLE defines several language objects types, that is, object that will be accessible as types inside the XCLE programming language. These are XCLE_Void, used as an undefined value, XCLE_Intg and XCLE_Fltp, providing the integer and floating point numeric types, XCLE_Strg that holds character string or binary data, XCLE_List which serves both as a collection structure, and a program structure, and XCLE_Code that provides the language's basic instructions, or primitives. These types inherit from a generic object type XCLE_Object, which stands for any language-level object.
No data is directly accessible inside these objects. Instead, constructors and accessors are provided, that check data integrity and help making memory management transparent. Object creation can happen in two ways: the use of an explicit constructor, of the form XCLE_<type>New(data), or the parsing of a valid string representation, through the XCLE_ObjectParse and XCLE_ListParse methods defined in the <Mod_parse.h> header file.
Language types definitions
The XCLE_<type>New(data) methods
For a few objects, the XCLE_<type>New(data) methods do not exist, and are replaced by XCLE_<type>Alloc() methods that take no argument.
The XCLE_VoidAlloc method creates a new XCLE_Void object. It takes no argument.
The XCLE_IntgNew method creates a new XCLE_Intg object. It takes a single argument, the integer number whose value will take the newly created object.
The XCLE_FltpNew method creates a new XCLE_Fltp object. It takes a single argument, the floating point number that will serve to initialize the object.
The XCLE_StrgNew method creates a new XCLE_Strg object. It takes a single argument, a C-style character string (i.e. a nul-char-terminated character array) copied into the new object.
The XCLE_ListAlloc method creates a new XCLE_List object. It takes no argument, and returns an empty list.
The XCLE_CodeAlloc should probably be used only by primitives library developers. It is useless per se, as the returned XCLE_Code object is devoid of any information such as primitive name, or handler. Executing a program containing this object will result in a runtime error. XCLE_Code objects should be created using the appropriate methods from the <XCLE/Mod_Parse.h> module, as detailed in the Parsing process section.
Provided a parsing context has been defined, containing primitives definitions and parsing parameters, the most simple way of creating objects is probably to parse them from their string representation, using methods such as XCLE_ObjectParse and XCLE_ListParse, whose use will be detailed in the Parsing section.
In the same context, the XCLE_ParseCtx_CodeByName method can be used instead of XCLE_CodeAlloc to obtain valid XCLE_Code objects. It asks for a defined parsing context, and a string that should be the name of a primitive registered in the parsing context. A fully functionnal XCLE_CodeAlloc object will be returned.
Reference counting and memory
The memory management system of XCLE reposes on references count. Each object has its own, that reflect the number of "ownership tokens" that were distributed for this object. Each module or code structure that makes use of an object and is susceptible to share it with other structures must have one such token on the object.
Increasing the reference count of an object means acquiring such a token, while relinquising this token bars this code section from any right to use this object anymore.
An object can be definitively freed only if its reference count is zero, that is, if no part of the program is using this object anymore.
Be wary of implicit (de-)referencing of objects included in compounds (lists elements, and code data segment). It will happen when the references on parent objects are modified, because ownership of the parent means also ownership of its components.
As we saw above, most types have their XCLE_<Type>Alloc constructors, or alternately XCLE_<Type>New. However, calling these constructors isn't enough to reference the new object, i.e. informing XCLE this object is in use. That, in turn, should be done using the XCLE_ObjectUpRef and XCLE_ObjectDnRef methods.
Referencing a new object (through XCLE_ObjectUpRef) is useless if its visibility will be restricted to the current block. It is recommanded whenever the object will persist after the end of the block. It is mandatory when the object will be duplicated, in part or whole (thing of the atoms in a list), in a container structure like XCLE_Stack or XCLE_Hash.
The same holds for dereferencing an object (through XCLE_ObjectDnRef) that has not been created in this block, and has become useless. As well, this is mandatory for objects deleted from a container structure.
Methods and calls from the XCLE library are a special case: as integral to the memory management system, they always return objects in the same reference state as they were given (or unreferenced for new objects), unless specified otherwise.
When an object is not used anymore in the block, XCLE_ObjectFree (or a type-specific method) must be called on it. If the object is not owned at this time, it will be effectively freed.
Role of support structures
(De-)referencing is mandatory for operations on containers structures, but most operations on containers will take care for you of referencing inserted objects, and dereferencing objects deleted. Containers structures are not objects per-se, and thus have no reference counts of their own.
Freeing these structures will dereference (once only) and free their objects. Take special care when using methods that return an object from a container without deleting it. If you then free the container, or simply empty it, this object will have been dereferenced. If it is not used anywere else, this object will then have reached a zero reference count, and have been freed.
Defining primitive templates through Code_Def
Registering primitives sets
What are exceptions?
Raised exception stack