We have 2 APIs, the C++ and the C API. Both are explained in this page.

C++ API

The C++ API is part of the Generator.hpp header. Most functionality is contained under the UTTE::Generator class, which is used to parse a document.

Loading a document

To load a document, initialise the UTTE::Generator class using the default constructor, then call the loadFromFile or loadFromString functions like this:

UTTE::Generator generator{};
generator.loadFromFile("test.tmpl");

The loadFromFile and loadFromString functions both have the same signature. They receive a string which will be the file location or the buffer to be used.

Both of these functions return an InitialisationResult struct, which looks like this:

enum InitialisationResult
{
    UTTE_INITIALISATION_RESULT_SUCCESS = 0,
    UTTE_INITIALISATION_RESULT_INVALID_UTF8 = 1,
    UTTE_INITIALISATION_RESULT_INVALID_FILE = 2,
};

It is used to check if there were any errors when trying to initialise the generator.

Parsing a document

Once a document is loaded, you can parse it with the parse member function. It returns a struct of type ParseResult, which looks like this:

struct ParseResult
{
    ParseResultStatus status;
    const std::string* result;

    Variable _internalBuffer;
};

The resulting string is the result member, which is a pointer to an std::string.

The status member is of type ParseResultStatus, which looks like this:

enum ParseResultStatus
{
    UTTE_PARSE_STATUS_SUCCESS = 0,
    UTTE_PARSE_STATUS_OUT_OF_BOUNDS = 1,
    UTTE_PARSE_STATUS_EXPECTED_TERMINATION = 2,
    UTTE_PARSE_STATUS_INVALID_VALUE = 3,
    UTTE_PARSE_STATUS_INVALID_TYPE = 4,
};

It can be used to do error checking.

Finally, the _internalBuffer variable contains rubbish intermediate internal data. You should not use it.

Adding additional variables and functions

You can add additional variables and functions by calling the pushVariable and pushFunction members, respectively.

Variables

The pushVariable function takes a constant reference to a Variable struct and a name for the variable. The Variable struct looks like this:

struct Variable
{
    std::string value;
    VariableTypeHint type = UTTE_VARIABLE_TYPE_HINT_NORMAL;
    ParseResultStatus status = UTTE_PARSE_STATUS_SUCCESS;

    bool _internalBoolComment = false;
};

Its members are the following:

  1. value - The value of the variable
  2. VariableTypeHint - A hint to the type of the variable
  3. status - The status of a variable when returning from a function. Used to tell the parser to shut down on error
  4. _internalBoolComment - Whether to treat the variable like a comment. Do not use

Tip

When returning an error from a function, use the UTTE_ERROR(x) macro. It only takes a single argument, which is the ParseResultStatus enum.

The VariableTypeHint enum looks like this:

enum VariableTypeHint
{
    UTTE_VARIABLE_TYPE_HINT_NORMAL = 0,
    UTTE_VARIABLE_TYPE_HINT_ARRAY = 1,
    UTTE_VARIABLE_TYPE_HINT_MAP = 2,
    UTTE_VARIABLE_TYPE_HINT_FUNCTION = 3,
};

A variable set to UTTE_VARIABLE_TYPE_HINT_NORMAL is a simple string.


The pushVariable function also returns a reference to a Function struct, which was pushed to the function registry, as variables are treated as functions.

The Function struct looks like this:

struct Function
{
    std::string name;
    std::function<Func> function = [](std::vector<Variable>&, UTTE::Generator*) -> Variable{ return {}; };
};

The name member is the name of the function, while the function member is the actual function pointer to be called. It is of type std::function<Func>, where Func is a type definition of Variable(std::vector<Variable>&, UTTE::Generator*).

Tip

If you want to change the value of a variable from a custom function, use the UTTE_VARIABLE_SET_NEW_VAL macro like this:

UTTE_VARIABLE_SET_NEW_VAL(key, a, a.first, UTTE_VARIABLE_TYPE_HINT_NORMAL);

In this example, the arguments are like this:

  1. key - The Function& returned by pushVariable
  2. a - A struct to pass to the internal lambda through the capture, in this case an std::pair
  3. a.first - The new value of the variable
  4. UTTE_VARIABLE_TYPE_HINT_NORMAL - The new type of the variable

Functions

Functions can be added by calling the pushFunction function. It takes a constant reference to a Function struct and returns a reference to itself, but in the function registry.

Updating arbitrary variables and functions

Generally, it's preferable to keep references to the values returned by pushVariable and pushFunction, but sometimes it's not possible. The setVariable and setFunction members of Generator can be used to update a function or variable from a name.

They both return void and their first argument is the name of the function or variable as a string. The second argument is a const Variable& or a const std::function<Func>&, for setVariable and setFunction respectively.

Creating arrays and maps

To push array and map variables to the generator, use the makeArray and makeMap static member functions of the Generator class. They take a const std::vector<std::string>& and const std::map<std::string, std::string>& respectively and both return a Variable struct that can be used directly with pushVariable

Returning arrays and maps from custom functions

When in a custom function, it may be convenient to return an array or map. This, however, requires you to store the given map or array beyond the lifetime of the given function. Instead of making you create your own custom registry to store these variables, the parser can create and store them for you automatically.

The UTTE::Generator::requestArrayWithGC and UTTE::Generator::requestMapWithGC functions return a reference to an array and map, respectively. The underlying variables have the same lifetime as the generator.

Getting the internal functions registry

Some special functions may need more control over what functions are part of the registry. For systems that want tighter security, this may even mean removing components of the standard library. To get full access to the functions registry call UTTE::Generator::getFunctionsRegistry.

It returns a reference to an array of UTTE::Function.

C API

The C API has a lot of overlap with the C++ one, therefore, make sure to read through the entire C++ API part of the page, before continuing to read about the C API. This guide will only cover parts of the API that are different from the C++ one.

Most functions that are part of the C API are the same as the one in the C++ API, except that the :: operator is replaced by a _, making the C++ function UTTE::Generator::loadFromFile into the C function UTTE_CGenerator_loadFromFile.

Creating the generator

In the C API, the generator is created by calling the UTTE_CGenerator_Allocate function. It returns a pointer of type UTTE_CGenerator. Since C generators are allocated on the heap, this handle needs to be freed using the UTTE_CGeneratorFree function.

All functions that are related to the generator take a UTTE_CGenerator* as their first argument.

The UTTE_CParseResult struct

The C alternative to the UTTE::ParseResult struct is UTTE_CParseReult. It is returned by UTTE_Generator_parse and looks like this:

struct UTTE_CParseResult
{
    UTTE_ParseResultStatus status;
    const char* result;
};

The only significant difference is the replacement of utte_string with const char* for the result member

Pushing variables and functions

Variables

To push a variable, call UTTE_CGenerator_pushVariable. It takes a pointer to the generator, as well as a pointer to a UTTE_CVariable struct and a name as a const char*.

The UTTE_CVariable struct is the alternative of the C++ UTTE::Variable struct, and looks like this:

struct UTTE_CVariable
{
    const char* value;
    UTTE_VariableTypeHint type;
    bool bDeallocate;
    UTTE_ParseResultStatus status;
};

The difference is that value is now a const char* and that a new member called bDeallocate is added. If the value member is heap-allocated, you can set bDeallocate to automatically free it after use when calling the following functions:

  1. UTTE_CGenerator_pushVariable
  2. UTTE_CGenerator_tryFreeCVariable

The pushVariable functions a UTTE_CFunctionHandle*, which is a handle to a UTTE::Function in the functions' registry. The following functions take it:

  1. UTTE_CGenerator_modify - Given a Function handle and a UTTE_CFunction struct as arguments, will change the underlying function to the data of the UTTE_CFunction argument. If the name field of the UTTE_CFunction struct is set to an empty string, the name of the function will not be changed
  2. UTTE_CGenerator_getName - Given a Function handle, returns the name of the function

The UTTE_CFunction struct is the C alternative of the UTTE::Function struct and looks like this:

struct UTTE_CFunction
{
    const char* name;
    UTTE_CFunctionCallback function;
    bool bDeallocate;
};

If name is set to a heap-allocated value, setting bDeallocate to true will free it in the following functions:

  1. UTTE_CGenerator_modify
  2. UTTE_CGenerator_pushFunction

The function field is of type UTTE_CFunctionCallback, which has the following signature: UTTE_CVariable()(UTTE_CVariable*, size_t size, UTTE_CGenerator*).

Functions

Functions are pushed using the UTTE_CGenerator_pushFunction function. The second argument needs to be a pointer to a UTTE_CFunction struct. It returns a UTTE_CFunctionHandle*, like pushVariable.

Setting variables and functions

The UTTE_CGenerator_setVariable and UTTE_CGenerator_setFunction functions allow the user to iterate the internal generator registry for a function using a name. If found, its data is replaced by the given UTTE_CVariable* and UTTE_CFunctionCallback arguments, respectively.

Both functions return true if a function is found and false if not.

UTTE_CGenerator_setVariable will deallocate the value field of the UTTE_CVariable struct if the bDeallocate boolean is set to true.

Generating arrays

To generate an array, call the UTTE_CGenerator_makeArray function. It takes a char** for the array and a size_t for its size.

It returns a UTTE_CVariable struct with a heap-allocated value. The bDeallocate member is set to true, so passing it to UTTE_CGenerator_pushVariable will free the memory automatically. Alternatively, use UTTE_CGenerator_tryFreeCVariable to free it manually.

Generating maps

To generate a map, call the UTTE_CGenerator_makeMap function. It takes a UTTE_CPair* for the array and a size_t for its size.

The UTTE_CPair struct stores a key and a value and looks like this:

struct UTTE_CPair
{
    char* key;
    char* val;
};

It returns a UTTE_CVariable struct with a heap-allocated value. The bDeallocate member is set to true, so passing it to UTTE_CGenerator_pushVariable will free the memory automatically. Alternatively, use UTTE_CGenerator_tryFreeCVariable to free it manually.