Skip to content

libiuab developer guide

OverMighty edited this page Apr 13, 2020 · 1 revision

Welcome to the developer guide for libiuab, the official "I use Arch btw" library for C/C++.

libiuab allows you to embed the "I use Arch btw" programming language into your C/C++ programs. More precisely, it mainly provides an "I use Arch btw" compiler, as well as a bytecode virtual machine to run compiled "I use Arch btw" programs. Although the library supports C++, it is written in C and therefore does not make use of namespaces or any other feature exclusive to C++.

This guide aims to get C/C++ developers started with libiuab.

C++ example

An example C++ program that uses libiuab can be found here.

Initializing and deinitializing structs

The following is true for any struct type TYPE defined in a libiuab header:

If there is a function named TYPE_init, structs of type TYPE must be initialized using a call to that function.

If there is a function named TYPE_fini, structs of type TYPE must be deinitialized using a call to that function once they are no longer used in your code. Structs that were dynamically allocated must be deinitialized before being deallocated, as TYPE_fini functions free the resources that were dynamically allocated by TYPE_init functions.

Error codes

Some functions defined by libiuab may fail. These functions return a value of the iuab_error enum type, which is defined in iuab/errors.h. In order to check if a call to one of those functions was successful, you should check if the returned value equals to IUAB_ERROR_SUCCESS.

The iuab/errors.h header also defines the following function, which takes a libiuab error code and returns a string describing the error:

char *iuab_strerror(enum iuab_error error);

Compiling "I use Arch btw" source code to bytecode

The iuab/compiler.h header provides a bytecode compiler for "I use Arch btw" source code. The bytecode generated by the compiler is not fully portable as multi-byte bytecode elements are written in the host machine's byte order.

The compiler reads source code from files. fmemopen() should be used if available on the target platform in order to compile a string of source code.

Here is an example usage in C: a function that takes a file containing "I use Arch btw" source code, compiles the source code it contains, and returns a pointer to the first byte of the bytecode generated by the compiler:

uint8_t *compile(FILE *src) {
    struct iuab_compiler *comp = malloc(sizeof(struct iuab_compiler));

    if (!comp) {
        fputs("memory allocation failure", stderr);
        return NULL;
    }

    /* Initialize the compiler for compilation of src */
    enum iuab_error result = iuab_compiler_init(comp, src);

    if (result != IUAB_ERROR_SUCCESS) {
        fprintf(stderr, "compiler init failure: %s\n", iuab_strerror(result));
        iuab_compiler_fini(comp);
        free(comp);
        return NULL;
    }

    /* The compiler will make this point to the generated bytecode */
    uint8_t *code;
    /* Run the compiler, compile src into code */
    result = iuab_compiler_run(comp, &code);

    if (result != IUAB_ERROR_SUCCESS) {
        fprintf(stderr, "error: %s at line %zu, col %zu\n",
            iuab_strerror(result), comp->token.line, comp->token.col);
        iuab_compiler_fini(comp);
        free(comp);
        return NULL;
    }

    iuab_compiler_fini(comp);
    free(comp);

    /* code must be free()'d once it is no longer used. */
    return code;
}

Executing "I use Arch btw" bytecode programs

The iuab/vm.h header provides a bytecode virtual machine for running compiled "I use Arch btw" bytecode programs. Its specification is available here.

Here is an example function written in C that takes a pointer to a block of "I use Arch btw" bytecode and executes it using the virtual machine:

void execute(const uint8_t *code) {
    struct iuab_vm *vm = malloc(sizeof(struct iuab_vm));

    if (vm == NULL) {
        fputs("memory allocation failure", stderr);
        return;
    }

    /* Initialize the VM for execution of code */
    iuab_vm_init(vm, code, stdin, stdout);

    /* Index of the last opcode processed by the VM in the bytecode */
    size_t last_op_pos;
    /* Run the VM, execute code */
    enum iuab_error result = iuab_vm_run(vm, &last_op_pos);

    if (result != IUAB_ERROR_SUCCESS) {
        fprintf(stderr, "error at 0x%zX: %s\n", last_op_pos,
            iuab_strerror(result));
    }

    free(vm);
}