4. Core Data Structures

The Worgle/Orgle program is very much a data-structure driven program. Understanding the hierarchy of data here will provide a clear picture for how the tangling works.

<<structs>>=
<<worgle_string>>
<<worgle_segment>>
<<worgle_block>>
<<worgle_blocklist>>
<<worgle_hashmap>>
<<worgle_file>>
<<worgle_filelist>>
<<worgle_textbuf>>
<<worgle_orglet>>
<<worgle_orgfile>>
<<worgle_struct>>

4.1. Top Level Struct

All Worgle operations are contained in a top-level struct called worgle_d. For the most part, this struct aims to be dynamically populated.

<<typedefs>>=
typedef struct worgle_d worgle_d;
<<worgle_struct>>=
struct worgle_d {
<<worgle_struct_contents>>
};

4.1.1. Worgle Initialization

Worgle data is initialized using the function worgle_init.

<<function_declarations>>=
void worgle_init(worgle_d *worg);
<<functions>>=
void worgle_init(worgle_d *worg)
{
<<worgle_init>>
}

4.1.2. Worgle Deallocation

When worgle is done, the program deallocates memory using the function worgle_free.

<<function_declarations>>=
void worgle_free(worgle_d *worg);
<<functions>>=
void worgle_free(worgle_d *worg)
{
    int i;
<<worgle_free>>
}

4.1.3. Worgle Data

4.1.3.1. Text Block

The block variable is used to store multi-line text blocks being parsed, such as those in a code block when the parser, such as when the parser is in MODE_CODE.

It may also be used to parse content while the parser in mode MODE_ORG.

<<worgle_struct_contents>>=
worgle_string block;

It is initialized to be an empty string.

<<worgle_init>>=
worgle_string_init(&worg->block);
4.1.3.2. Segment Block

Like the text block, but for holding segments instead. Only included in the full-version.

<<worgle_struct_contents>>=
#ifndef WORGLITE
worgle_string segblock;
#endif

It is initialized to be an empty string.

<<worgle_init>>=
#ifndef WORGLITE
worgle_string_init(&worg->segblock);
#endif
4.1.3.3. Current Line

The starting line number of the current block is stored in a variable called curline.

<<worgle_struct_contents>>=
long curline;

The current line is initialized to be negative value to mark that it has not been set yet.

<<worgle_init>>=
worg->curline = -1;
4.1.3.4. Block Started Flag

The block started flag is used by the parser to check whether or not a code block was started on the last iteration.

<<worgle_struct_contents>>=
int block_started;

It is set to be FALSE (0).

<<worgle_init>>=
worg->block_started = 0;
4.1.3.5. New Content Flag

This gets set any time Worgle is ready to begin a new content block.

<<worgle_struct_contents>>=
#ifndef WORGLITE
int new_content;
#endif

It is enabled by default.

<<worgle_init>>=
#ifndef WORGLITE
worg->new_content = 1;
#endif
4.1.3.6. Dictionary

All code blocks are stored in a dictionary, also referred to here as a hash map.

<<worgle_struct_contents>>=
worgle_hashmap dict;

The dictionary is initialized using the function worgle_hashmap_init.

<<worgle_init>>=
worgle_hashmap_init(&worg->dict);

When free-ing time comes around, the hashmap will free itself using the function worgle_hashmap_free.

<<worgle_free>>=
worgle_hashmap_free(&worg->dict);
4.1.3.7. File List

All files to be written to are stored in a local file list called flist.

<<worgle_struct_contents>>=
worgle_filelist flist;

Initialization.

<<worgle_init>>=
worgle_filelist_init(&worg->flist);

Destruction.

<<worgle_free>>=
worgle_filelist_free(&worg->flist);
4.1.3.8. Text Buffer

Text files are loaded into buffers, encapsulated as a type worgle_textbuf. The currently used text buffer is stored in the variable curbuf. Buffers are stored in an array of text buffers known as buflist.

<<worgle_struct_contents>>=
worgle_textbuf *curbuf;
worgle_textbuf *buffers;
int nbuffers;

The loaded happens after initialization, so the buffer is set to be NULL for now.

<<worgle_init>>=
worg->curbuf = NULL;
worg->buffers = NULL;
worg->nbuffers = 0;

If the buffer is non-null, the memory will be freed.

<<worgle_free>>=
for(i = 0; i < worg->nbuffers; i++) {
    worgle_textbuf_free(&worg->buffers[i]);
}
if(worg->nbuffers > 0) free(worg->buffers);
4.1.3.9. Current Block

A pointer to the currently populated code block is stored in a variable called curblock.

<<worgle_struct_contents>>=
worgle_block *curblock;

There is no block on startup, so set it to be NULL.

<<worgle_init>>=
worg->curblock = NULL;
4.1.3.10. Line Number

The currently parsed line number is stored in a variable called linum.

<<worgle_struct_contents>>=
size_t linum;

The line number is incremented, so the starting value starts at 0. Line 1 is the first line. Do not be tempted to set this to be -1, because it won't work.

<<worgle_init>>=
worg->linum = 0;
4.1.3.11. Orgfile Array

The orgfile array keeps track of data representations of orgfiles to be tangled.

4.1.3.11.1. Declaration + Initialization

<<worgle_struct_contents>>=
worgle_orgfile *orgs;
worgle_orgfile *curorg;
<<worgle_init>>=
worg->orgs = NULL;
worg->curorg = NULL;
4.1.3.11.2. Freeing

Org files allocated must be freed.

<<worgle_free>>=
if(worg->orgs != NULL && worg->nbuffers > 0) {
    for(i = 0; i < worg->nbuffers; i++) {
        worgle_orgfile_free(&worg->orgs[i]);
    }
    free(worg->orgs);
}
4.1.3.11.3. Appending an Org file

This happens when the main file list is being populated.

<<append_org_file>>=
if(worg->nbuffers == 1) {
    worg->orgs = calloc(1, sizeof(worgle_orgfile));
} else {
    worg->orgs = realloc(worg->orgs,
                         sizeof(worgle_orgfile) *
                         worg->nbuffers);
}
if(worg->nbuffers > 0) {
    worgle_orgfile_init(&worg->orgs[worg->nbuffers - 1]);
}
4.1.3.12. Program ID

The program id is a integer value primarily used to distinguish itself from other programs in a generated database. This functionality allows portions of a program to be incrementally updated/written to a database.

<<worgle_struct_contents>>=
int prog;
<<worgle_init>>=
worg->prog = 0;

4.2. String

A string is a wrapper around a raw char pointer and a size. This is used as the base string literal.

<<worgle_string_contents>>=
char *str;
size_t size;
<<typedefs>>=
typedef struct worgle_string worgle_string;
<<worgle_string>>=
struct worgle_string {
<<worgle_string_contents>>
};

4.2.1. Reset or initialize a string

Strings in worgle are reset with the function worgle_string_reset.

<<worgle_string_init>>=
str->str = NULL;
str->size = 0;
<<function_declarations>>=
void worgle_string_reset(worgle_string *str);
<<functions>>=
void worgle_string_reset(worgle_string *str)
{
<<worgle_string_init>>
}

A string being initialized is identical to a string being reset. The function worgle_string_init is just a wrapper around worgle_string_reset.

<<function_declarations>>=
void worgle_string_init(worgle_string *str);
<<functions>>=
void worgle_string_init(worgle_string *str)
{
    worgle_string_reset(str);
}

4.2.2. Writing a String

A string is written to a particular filehandle with the function worgle_string_write.Worgle strings are not zero-terminated and can't be used in functions like printf.


<<function_declarations>>=
int worgle_string_write(FILE *fp, worgle_string *str);

This function is a wrapper around a call to fwrite.

<<functions>>=
int worgle_string_write(FILE *fp, worgle_string *str)
{
    return fwrite(str->str, 1, str->size, fp);
}

4.3. Segment

A segment turns a string into a linked list component that has a type. A segment type flag can either be a text chunk or a reference.

<<typedefs>>=
typedef struct worgle_segment worgle_segment;
<<worgle_segment>>=
enum {
<<worgle_segment_types>>
};
struct worgle_segment {
<<worgle_segment_contents>>
};
<<worgle_segment_contents>>=
int type;
worgle_string str;
<<worgle_segment_line_control>>
worgle_segment *nxt;

Segments also keep track of where they are in the original org file. This information can be used to generate line control preprocessor commands for C/C++.

<<worgle_segment_line_control>>=
size_t linum;
worgle_string *filename;

4.3.1. Text Chunk Type

A text chunk is a literal string of text.

When a text chunk segment is processed, it gets written to file directly.

<<worgle_segment_types>>=
SEGTYPE_TEXT,

4.3.2. Reference Type

A reference contains a string reference to another block.

When a reference segment gets processed, it looks up the reference and processes all the segements in that code block.

<<worgle_segment_types>>=
SEGTYPE_REFERENCE

4.3.3. Initializing a Segment

A segment is initialized with the function worgle_segment_init.

<<function_declarations>>=
void worgle_segment_init(worgle_segment *s,
                        int type,
                        worgle_string *str,
                        worgle_string *filename,
                        size_t linum);
<<functions>>=
void worgle_segment_init(worgle_segment *s,
                        int type,
                        worgle_string *str,
                        worgle_string *filename,
                        size_t linum)
{
<<worgle_segment_init>>
}
<<worgle_segment_init>>=
s->type = type;
s->str = *str;
s->filename = filename;
s->linum = linum;
s->nxt = NULL;

4.3.4. Writing a Segment

A segment is written to a file handle using the function worgle_segment_write. In addition to taking in a filehandle and segment, a hashmap is also passed in in the event that the segment is a reference.

On sucess, the function returns TRUE (1). On failure, FALSE (0).

<<function_declarations>>=
int worgle_segment_write(worgle_segment *s, worgle_hashmap *h, FILE *fp);

Different behaviors happen depending on the segment type.

If the segment is a chunk of text (SEGTYPE_TEXT), then the string is written. If the use_debug global variable is enabled, then C preprocessor macros are written indicating the position from the original file. This only needs to happen for text blocks and not references.

If the segment is a reference (SEGTYPE_REFERENCE), the function attempts to look up a block and write it to disk. If it cannot find the reference, a warning is flashed to screen. If the warning mode is soft, the error code returns TRUE. If warning errors are turned on, it returns FALSE.

<<functions>>=
int worgle_segment_write(worgle_segment *s, worgle_hashmap *h, FILE *fp)
{
    worgle_block *b;
    if(s->type == SEGTYPE_TEXT) {
        if(use_debug) {
            fprintf(fp, "#line %lu \"", s->linum);
            worgle_string_write(fp, s->filename);
            fprintf(fp, "\"\n");
        }
        worgle_string_write(fp, &s->str);
    } else {
        if(!worgle_hashmap_find(h, &s->str, &b)) {
            fprintf(stderr, "Warning: could not find reference segment '");
            worgle_string_write(stderr, &s->str);
            fprintf(stderr, "'\n");
            if(use_warnings == 2) {
                return 0;
            } else {
                return 1;
            }
        }
        return worgle_block_write(b, h, fp);
    }

    return 1;
}

4.3.5. Segment Type Checks

All segment types can be checked with a few functions, without needing to know any of the type macros.

<<function_declarations>>=
int worgle_segment_is_text(worgle_segment *s);
int worgle_segment_is_reference(worgle_segment *s);
<<functions>>=
int worgle_segment_is_text(worgle_segment *s)
{
    return s->type == SEGTYPE_TEXT;
}

int worgle_segment_is_reference(worgle_segment *s)
{
    return s->type == SEGTYPE_REFERENCE;
}

4.4. Code Block

A code block is a top-level unit that stores some amount of code. It is made up of a list of segments. Every code block has a unique name.

<<typedefs>>=
typedef struct worgle_block worgle_block;
<<worgle_block>>=
struct worgle_block {
<<worgle_block_contents>>
};
<<worgle_block_contents>>=
int nsegs;
worgle_segment *head;
worgle_segment *tail;
worgle_string name;
int am_i_used;
worgle_block *nxt;

4.4.1. Initializing a code block

A worgle code block is initialized using the function worgle_block_init.

<<function_declarations>>=
void worgle_block_init(worgle_block *b);

The initialization will zero out all the variables related to the segment linked list, as well as initialize the string holding the name of the block.

<<functions>>=
void worgle_block_init(worgle_block *b)
{
<<worgle_block_init>>
}
<<worgle_block_init>>=
b->nsegs = 0;
b->head = NULL;
b->tail = NULL;
b->nxt = NULL;
b->am_i_used = 0;
worgle_string_init(&b->name);

4.4.2. Freeing a code block

A code block is freed using the function worgle_block_free.

<<function_declarations>>=
void worgle_block_free(worgle_block *lst);

This function iterates through the segment linked list contained inside the block, and frees each one. Since there is nothing to free below a segment, the standard free function is called directly.

<<functions>>=
void worgle_block_free(worgle_block *lst)
{
    worgle_segment *s;
    worgle_segment *nxt;
    int n;
    s = lst->head;
    for(n = 0; n < lst->nsegs; n++) {
        nxt = s->nxt;
        free(s);
        s = nxt;
    }
}

4.4.3. Appending a segment to a code block

A generic segment is appended to a code block with the function worgle_block_append_segment.

The block b, name of the block str, and type type are mandatory parameters which describe the segment. The location in the file is also required, so the line number linum and name of file filename are also provided as well. This function is called inside of a type-specific append function instead of being called directly.

<<function_declarations>>=
worgle_segment* worgle_block_append_segment(worgle_block *b,
                                            worgle_string *str,
                                            int type,
                                            size_t linum,
                                            worgle_string *filename);

It is worthwhile to note that it is in this function that a data segment type gets allocated.

<<functions>>=
worgle_segment* worgle_block_append_segment(worgle_block *b,
                                            worgle_string *str,
                                            int type,
                                            size_t linum,
                                            worgle_string *filename)
{
    worgle_segment *s;
    s = malloc(sizeof(worgle_segment));
    if(b->nsegs == 0) {
        b->head = s;
        b->tail = s;
    }
    worgle_segment_init(s, type, str, filename, linum);
    b->tail->nxt = s;
    b->tail = s;
    b->nsegs++;
<<store_last_seg>>
    return s;
}
4.4.3.1. Appending a string segment

A string segment is appended to a code block using the function worgle_block_append_string.

<<function_declarations>>=
worgle_segment* worgle_block_append_string(worgle_block *b,
                               worgle_string *str,
                               size_t linum,
                               worgle_string *filename);
<<functions>>=
worgle_segment* worgle_block_append_string(worgle_block *b,
                                           worgle_string *str,
                                           size_t linum,
                                           worgle_string *filename)
{
    return worgle_block_append_segment(b, str, SEGTYPE_TEXT, linum, filename);
}
4.4.3.2. Appending a reference segment

A reference segment is appended to a code block using the function worgle_block_append_reference.

<<function_declarations>>=
worgle_segment* worgle_block_append_reference(worgle_block *b,
                                              worgle_string *str,
                                              size_t linum,
                                              worgle_string *filename);
<<functions>>=
worgle_segment* worgle_block_append_reference(worgle_block *b,
                                              worgle_string *str,
                                              size_t linum,
                                              worgle_string *filename)
{
    return worgle_block_append_segment(b, str, SEGTYPE_REFERENCE,
                                       linum, filename);
}

4.4.4. Appending a code block to a code block

In both CWEB and Org-tangle, existing code blocks can be appeneded to in different sections. Because of how this program works, this functionality comes for free.

It can be useful to be able to differentiate between different code block sections. This is done using a variable called nblocks, which keeps track of the number of blocks in a code block.

<<worgle_block_contents>>=
int nblocks;
<<worgle_block_init>>=
b->nblocks = 0;

Every time a new NAME'd block appears, the nblock variable gets incremented. The current nblockvalue is used as a position value that gets stored in the worgle_orglet_blkref.


<<increment_nblocks>>=
worg->curblock->nblocks++;

4.4.5. Writing a code block to filehandle

Writing a code block to a filehandle can be done using the function worgle_block_write. In addition to the file handle fp, an org block requires a hashmap, which is required in the lower level function orgle_segment_write for expanding code references.

This function returns a boolean TRUE (1) on success or FALSE (0) on failure.

<<function_declarations>>=
int worgle_block_write(worgle_block *b, worgle_hashmap *h, FILE *fp);

A code block iterates it's segment list, writing each segment to disk. A block will also be marked as being used, which is useful for supplying warning information later.

<<functions>>=
int worgle_block_write(worgle_block *b, worgle_hashmap *h, FILE *fp)
{
    worgle_segment *s;
    int n;
    s = b->head;
    b->am_i_used = 1;
    for(n = 0; n < b->nsegs; n++) {
        if(!worgle_segment_write(s, h, fp)) return 0;
        s = s->nxt;
    }

    return 1;
}

4.4.6. Keeping track of the last segment

The id of the last segment appended is kept track of. This done for the benefit of worgle_orglet_blkref.

<<worgle_block_contents>>=
int last_seg;
<<worgle_block_init>>=
b->last_seg = -1;

When a new block reference is created, the id of the last appended segment is saved. Segments need to happen after the id is created, which happens to be in two places: one for appending reference segments , and the other for appending string segments.

<<store_last_seg>>=
/* TODO: remove this */
/* b->last_seg = s->id; */
<<store_last_string_id>>=
worg->curblock->last_seg = seg->id;
<<store_last_reference_id>>=
worg->curblock->last_seg = seg->id;


This id, if a positive value, would be the last segment of the previous block. This information can be used later by worgmap to help extrapolate which segments belong to which blocks.

The first segment of a given code subblock N is obtained by looking at the stored segment id, and then finding the next segment. The last segment is obtained by looking at the id stored in the next subblock N + 1.

4.5. Code Block List

A code block list is a linked list of blocks, which is used inside of a hash map.

<<typedefs>>=
typedef struct worgle_blocklist worgle_blocklist;
<<worgle_blocklist>>=
struct worgle_blocklist {
    int nblocks;
    worgle_block *head;
    worgle_block *tail;
};

4.5.1. Block List Initialization

A block list is initialized using the function worgle_blocklist_init.

<<function_declarations>>=
void worgle_blocklist_init(worgle_blocklist *lst);
<<functions>>=
void worgle_blocklist_init(worgle_blocklist *lst)
{
    lst->head = NULL;
    lst->tail = NULL;
    lst->nblocks = 0;
}

4.5.2. Freeing a Block List

Blocks allocated by the block list are freed using the function worgle_blocklist_free.

<<function_declarations>>=
void worgle_blocklist_free(worgle_blocklist *lst);
<<functions>>=
void worgle_blocklist_free(worgle_blocklist *lst)
{
    worgle_block *b;
    worgle_block *nxt;
    int n;
    b = lst->head;
    for(n = 0; n < lst->nblocks; n++) {
        nxt = b->nxt;
        worgle_block_free(b);
        free(b);
        b = nxt;
    }
}

4.5.3. Appending a Block

An allocated block is appended to a block list using the function worgle_blocklist_append.

<<function_declarations>>=
void worgle_blocklist_append(worgle_blocklist *lst, worgle_block *b);
<<functions>>=
void worgle_blocklist_append(worgle_blocklist *lst, worgle_block *b)
{
    if(lst->nblocks == 0) {
        lst->head = b;
        lst->tail = b;
    }
    lst->tail->nxt = b;
    lst->tail = b;
    lst->nblocks++;
}

4.6. Hash Map

A hash map is a key-value data structure used as a dictionary for storing references to code blocks.

<<typedefs>>=
typedef struct worgle_hashmap worgle_hashmap;
<<worgle_hashmap>>=
#define HASH_SIZE 256
struct worgle_hashmap {
    worgle_blocklist blk[HASH_SIZE];
    int nwords;
};

4.6.1. Hash map Initialization

A hash map is initialized using the function worgle_hashmap_init

<<function_declarations>>=
void worgle_hashmap_init(worgle_hashmap *h);

A hashmap is composed of an array of block lists which must be initialized.

<<functions>>=
void worgle_hashmap_init(worgle_hashmap *h)
{
    int n;
    h->nwords = 0;
    for(n = 0; n < HASH_SIZE; n++) {
        worgle_blocklist_init(&h->blk[n]);
    }
}

4.6.2. Freeing a Hash Map

Information allocated inside the hash map is freed using the function worgle_hashmap_free.

<<function_declarations>>=
void worgle_hashmap_free(worgle_hashmap *h);

To free a hash map is to free each block list in the array.

<<functions>>=
void worgle_hashmap_free(worgle_hashmap *h)
{
    int n;
    for(n = 0; n < HASH_SIZE; n++) {
        worgle_blocklist_free(&h->blk[n]);
    }
}

4.6.3. Looking up an entry

A hashmap lookup can be done with the function worgle_hashmap_find. This will attempt to look for a value with the key value name, and save it in the block pointer b. If nothing is found, the function returns FALSE (0). On success, TRUE (1).

<<function_declarations>>=
int worgle_hashmap_find(worgle_hashmap *h, worgle_string *name, worgle_block **b);
<<functions>>=
<<hashmap_hasher>>
int worgle_hashmap_find(worgle_hashmap *h, worgle_string *name, worgle_block **b)
{
    int pos;
    worgle_blocklist *lst;
    int n;
    worgle_block *blk;
    pos = hash(name->str, name->size);
    lst = &h->blk[pos];

    blk = lst->head;
    for(n = 0; n < lst->nblocks; n++) {
        if(name->size == blk->name.size) {
            if(!strncmp(name->str, blk->name.str, name->size)) {
                *b = blk;
                return 1;
            }
        }
        blk = blk->nxt;
    }
    return 0;
}

Like any hashmap, a hashing algorithm is used to to compute which list to place the entry in. This is one I've used on a number of projects now.

<<hashmap_hasher>>=
static int hash(const char *str, size_t size)
{
    unsigned int h = 5381;
    size_t i = 0;

    for(i = 0; i < size; i++) {
        h = ((h << 5) + h) ^ str[i];
        h %= 0x7FFFFFFF;
    }

    return h % HASH_SIZE;
}

4.6.4. Getting an entry

To "get" an entry means to return a block if it exists or not. Return an entry that exists, or make a new one. This can be done with the function worgle_hashmap_get.

<<function_declarations>>=
worgle_block * worgle_hashmap_get(worgle_hashmap *h, worgle_string *name);
<<functions>>=
worgle_block * worgle_hashmap_get(worgle_hashmap *h, worgle_string *name)
{
    worgle_block *b;
    worgle_blocklist *lst;
    int pos;

    if(worgle_hashmap_find(h, name, &b)) return b;
    pos = hash(name->str, name->size);
    b = NULL;
    b = malloc(sizeof(worgle_block));
    worgle_block_init(b);
    b->name = *name;
    lst = &h->blk[pos];
    worgle_blocklist_append(lst, b);
    return b;
}

4.7. File

A worgle file is an abstraction for a single file worgle will write to. Every file has a filename, and a top-level code block. A worgle does not have a filehandle. Files will only be created at the generation stage.

<<typedefs>>=
typedef struct worgle_file worgle_file;
<<worgle_file>>=
struct worgle_file {
<<worgle_file_contents>>
};
<<worgle_file_contents>>=
worgle_string filename;
worgle_block *top;
worgle_file *nxt;

4.7.1. Writing A File to a filehandle

A file is writen to a filehandle using the function worgle_file_write. A hashmap is also required because it contains all the named code blocks needed for any code expansion.

<<function_declarations>>=
int worgle_file_write(worgle_file *f, worgle_hashmap *h);

A filehandle is opened, the top-most code block is written using worgle_block_write, and then the file is closed.

Because worgle strings are not zero terminated, they must be copied to a temporary string buffer with a null terminator. Any filename greater than 127 characters will be truncated.

<<functions>>=
int worgle_file_write(worgle_file *f, worgle_hashmap *h)
{
    FILE *fp;
    char tmp[128];
    size_t n;
    size_t size;
    int rc;

    if(f->filename.size > 128) size = 127;
    else size = f->filename.size;
    for(n = 0; n < size; n++) tmp[n] = f->filename.str[n];
    tmp[size] = 0;

    fp = fopen(tmp, "w");

    rc = worgle_block_write(f->top, h, fp);

    fclose(fp);
    return rc;
}

4.8. The File List

A file list is a linked list of worgle files.

<<typedefs>>=
typedef struct worgle_filelist worgle_filelist;
<<worgle_filelist>>=
struct worgle_filelist {
    worgle_file *head;
    worgle_file *tail;
    int nfiles;
};

4.8.1. Initializing a file list

A file list is zeroed out and initialized using the function worgle_filelist_init.

<<function_declarations>>=
void worgle_filelist_init(worgle_filelist *flist);
<<functions>>=
void worgle_filelist_init(worgle_filelist *flist)
{
    flist->head = NULL;
    flist->tail = NULL;
    flist->nfiles = 0;
}

4.8.2. Freeing a file list

A filelist is freed using the function worgle_filelist_free.

<<function_declarations>>=
void worgle_filelist_free(worgle_filelist *flist);
<<functions>>=
void worgle_filelist_free(worgle_filelist *flist)
{
    worgle_file *f;
    worgle_file *nxt;
    int n;
    f = flist->head;
    for(n = 0; n < flist->nfiles; n++) {
        nxt = f->nxt;
        free(f);
        f = nxt;
    }
}

4.8.3. Appending a file to a file list

A file is appended to the file list using the function worgle_filelist_append. The name, as well as the well as the top-level code block are required here.

<<function_declarations>>=
worgle_file* worgle_filelist_append(worgle_filelist *flist,
                           worgle_string *name,
                           worgle_block *top);
<<functions>>=
worgle_file* worgle_filelist_append(worgle_filelist *flist,
                           worgle_string *name,
                           worgle_block *top)
{
    worgle_file *f;
    f = malloc(sizeof(worgle_file));
    f->filename = *name;
    f->top = top;
    f->nxt = NULL;
<<worgle_file_init_id>>

    if(flist->nfiles == 0) {
        flist->head = f;
        flist->tail = f;
    }
    flist->tail->nxt = f;
    flist->tail = f;
    flist->nfiles++;
    return f;
}

4.8.4. Writing a filelist to disk

A file list can be appended using the function worgle_filelist_write.

A hashmap containing all named code blocks all that is required.

<<function_declarations>>=
int worgle_filelist_write(worgle_filelist *flist, worgle_hashmap *h);
<<functions>>=
int worgle_filelist_write(worgle_filelist *flist, worgle_hashmap *h)
{
    worgle_file *f;
    int n;

    f = flist->head;
    for(n = 0; n < flist->nfiles; n++) {
        if(!worgle_file_write(f, h)) return 0;
        f = f->nxt;
    }

    return 1;
}

4.9. Text Buffer

Files in memory are stored in a text buffer known as a worgle_textbuf.

<<typedefs>>=
typedef struct worgle_textbuf worgle_textbuf;
<<worgle_textbuf>>=
struct worgle_textbuf {
    char *buf;
    size_t size;
    worgle_string filename;
};

4.9.1. Zeroing out a text buffer

A text buffer is zeroed out with the function worgle_textbuf_zero. This is action will set things to be NULL an zero.

<<function_declarations>>=
void worgle_textbuf_zero(worgle_textbuf *txt);
<<functions>>=
void worgle_textbuf_zero(worgle_textbuf *txt)
{
    txt->buf = NULL;
    worgle_string_init(&txt->filename);
    txt->size = 0;
}

4.9.2. Initializing a text buffer

A text buffer is fully initialized with the function worgle_textbuf_init.

<<function_declarations>>=
void worgle_textbuf_init(worgle_textbuf *txt,
                         char *buf,
                         size_t bufsize);
<<functions>>=
void worgle_textbuf_init(worgle_textbuf *txt,
                         char *buf,
                         size_t bufsize)
{
    txt->buf = buf;
    txt->size = bufsize;
}

4.9.3. Freeing a text buffer

A text buffer is in charge of freeing a buffer it holds. This is done with the function worgle_textbuf_free. It is assumed that the memory for the filename string is handled elsewhere. It is also assumed that the buffer was allocated via system malloc, so it uses the system free function.

<<function_declarations>>=
void worgle_textbuf_free(worgle_textbuf *txt);
<<functions>>=
void worgle_textbuf_free(worgle_textbuf *txt)
{
    if(txt->buf != NULL) free(txt->buf);
    worgle_textbuf_zero(txt);
}

4.10. UUID

Every data structure has a Universally Unique Identifier (UUID). This concept was added later on in order to better accomodate the Worgle Database interface.

4.10.1. Worgle Long Typedef

All UUIDs have a type of worgle_long, which is set to be an unsigned long. UUIDs begin at 1, with 0 indicating an uninitialized or unset ID number.

<<typedefs>>=
typedef unsigned long worgle_long;

4.10.2. Top-level UUID counter

UUID management is done via a reference counter called UUID_count.

<<worgle_struct_contents>>=
worgle_long UUID_count;

It is initialized to be 1, the first valid UUID. When using the counter, the Worgle process will return then increment. This will ensure that the UUID will be valid no matter what.

<<worgle_init>>=
worg->UUID_count = 1;

A new UUID is generated by returning the UUID_counter, then incrementing it.

<<function_declarations>>=
worgle_long worgle_uuid_new(worgle_d *worg);
<<functions>>=
worgle_long worgle_uuid_new(worgle_d *worg)
{
    return worg->UUID_count++;
}

# # #

4.10.3. Segment ID

A segment ID is retrieved using worgle_segment_id_get and set using worgle_segment_id_set. This is the lowest level component where a UUID is required. The worgle_string type is lower than a worgle_segment, but it is always encapsulated in a worgle_segment.

<<function_declarations>>=
worgle_long worgle_segment_id_get(worgle_segment *s);
void worgle_segment_id_set(worgle_segment *s, worgle_long id);
<<worgle_segment_contents>>=
worgle_long id;
<<worgle_segment_init>>=
s->id = 0;
<<functions>>=
worgle_long worgle_segment_id_get(worgle_segment *s)
{
    return s->id;
}

void worgle_segment_id_set(worgle_segment *s, worgle_long id)
{
    s->id = id;
}

Segment UUIDs are assigned in two places: when a reference is assigned, and when string is assigned.

<<worgle_segment_string_set_id>>=
worgle_segment_id_set(seg, worgle_uuid_new(worg));
<<worgle_segment_reference_set_id>>=
worgle_segment_id_set(seg, worgle_uuid_new(worg));

4.10.4. Block ID

A block ID is retrieved using worgle_block_id_get and set using worgle_block_id_set.

<<function_declarations>>=
worgle_long worgle_block_id_get(worgle_block *b);
void worgle_block_id_set(worgle_block *b, worgle_long id);
<<worgle_block_contents>>=
worgle_long id;
<<worgle_block_init>>=
b->id = 0;
<<functions>>=
worgle_long worgle_block_id_get(worgle_block *b)
{
    return b->id;
}

void worgle_block_id_set(worgle_block *b, worgle_long id)
{
    b->id = id;
}

The worgle block ID is set inside of worgle_begin_block when a new block is started. In this section a previously allocated block is retrieved, or a new block is created entirely. A newly allocated block will have an id of 0. If this is the case, the block will be assigned a UUID. The new block also has a string name, so this needs to be assigned as well.

<<worgle_block_set_id>>=
if (worgle_block_id_get(worg->curblock) == 0) {
    worgle_block_id_set(worg->curblock, worgle_uuid_new(worg));
}

4.10.5. File ID

<<worgle_file_contents>>=
worgle_long id;
<<worgle_file_init_id>>=
f->id = 0;
<<function_declarations>>=
void worgle_file_id_set(worgle_file *f, worgle_long id);
<<functions>>=
void worgle_file_id_set(worgle_file *f, worgle_long id)
{
    f->id = id;
}
<<worgle_file_set_id>>=
worgle_file_id_set(f, worgle_uuid_new(worg));

4.10.6. Orglet ID

<<worgle_orglet_id>>=
worgle_long id;
<<worgle_orglet_id_init>>=
orglet->id = 0;
<<function_declarations>>=
void worgle_orglet_id_set(worgle_orglet *orglet,
                           worgle_long id);
<<functions>>=
void worgle_orglet_id_set(worgle_orglet *orglet,
                           worgle_long id)
{
    orglet->id = id;
}
<<worgle_orglet_set_id>>=
worgle_orglet_id_set(orglet, worgle_uuid_new(worg));

4.11. Org File

A worgle_orgfile is a data type for an org file, represented as a list of worgle_orglets.

4.11.1. Struct

<<typedefs>>=
typedef struct worgle_orgfile worgle_orgfile;
<<worgle_orgfile>>=
struct worgle_orgfile {
    worgle_orglet *head;
    worgle_orglet *tail;
    worgle_orgfile *prev;
    int size;
};

4.11.2. DONE Initialization

CLOSED: [2019-09-12 Thu 07:12] An org file type is initialized with worgle_orgfile_init.

<<function_declarations>>=
void worgle_orgfile_init(worgle_orgfile *org);
<<functions>>=
void worgle_orgfile_init(worgle_orgfile *org)
{
    org->head = NULL;
    org->tail = NULL;
    org->size = 0;
    org->prev = NULL;
}

4.11.3. DONE Freeing

CLOSED: [2019-09-12 Thu 08:27] Freed with worgle_orgfile_free.

<<function_declarations>>=
void worgle_orgfile_free(worgle_orgfile *org);
<<functions>>=
void worgle_orgfile_free(worgle_orgfile *org)
{
    worgle_orglet *ent;
    worgle_orglet *nxt;
    int n;
    if (org->size > 0) {
        ent = org->head;
        for (n = 0; n < org->size; n++) {
            nxt = ent->next;
            worgle_orglet_free(ent);
            ent = nxt;
        }
    }
}

4.11.4. Appending To File

Operations to append things to files.

4.11.4.1. Appending an orglet

This operation applies a general orglet to a worgle_orgfile. This orglet should be initialized and allocated by this point.

Most of the time, this is called indirectly through other functions.

<<function_declarations>>=
void worgle_orgfile_append(worgle_d *worg,
                           worgle_orgfile *file,
                           worgle_orglet *orglet);

To ensure that org content for a program spread out multiple files get linked properly, a prev file entry is utilized. If it isn't NULL, the tail of the previous list is linked to the head of the current list.

<<functions>>=
void worgle_orgfile_append(worgle_d *worg,
                           worgle_orgfile *file,
                           worgle_orglet *orglet)
{
    if (file->size <= 0) {
        file->head = orglet;
        if (file->prev != NULL) {
            file->prev->tail->next = file->head;
        }
        file->tail = orglet;
    }

    file->tail->next = orglet;
    file->tail = orglet;
    file->size++;
<<worgle_orglet_set_id>>
}
4.11.4.2. DONE Appending a header

CLOSED: [2019-09-12 Thu 08:27] Appends a header to file.

<<function_declarations>>=
void worgle_orgfile_append_header(worgle_d *worg,
                                  worgle_string *header,
                                  int lvl);
<<functions>>=
void worgle_orgfile_append_header(worgle_d *worg,
                                  worgle_string *header,
                                  int lvl)
{
    worgle_orglet *orglet;
    worgle_orglet_header *h;
    worgle_orgfile *f;
    size_t linum;

    f = worg->curorg;
    linum = worg->linum;

    orglet = calloc(1, sizeof(worgle_orglet));
    h = calloc(1, sizeof(worgle_orglet_header));

    h->str = *header;
    h->lvl = lvl;
    worgle_orglet_init(orglet);

    orglet->type = ORGLET_HEADER;
    orglet->ud = h;
    orglet->linum = linum;
    worgle_orgfile_append(worg, f, orglet);
}
4.11.4.3. DONE Appending Content

CLOSED: [2019-12-10 Tue 15:49] Appends content block to an org file.

<<function_declarations>>=
void worgle_orgfile_append_content(worgle_d *worg,
                                   worgle_string *text);
<<functions>>=
void worgle_orgfile_append_content(worgle_d *worg,
                                   worgle_string *text)
{
    worgle_orglet *orglet;
    worgle_orglet_content *c;
    worgle_orgfile *f;
    size_t linum;

    if (text->size == 0) return;
    if (text->size == 1) return;

    f = worg->curorg;
    linum = worg->linum;

    orglet = calloc(1, sizeof(worgle_orglet));
    c = calloc(1, sizeof(worgle_orglet_content));

    c->text = *text;

    /* printf("CONTENT(%d): '", c->text.size); */
    /* fwrite(c->text.str, 1, c->text.size, stdout); */
    /* printf("'\n"); */

    worgle_orglet_init(orglet);

    orglet->type = ORGLET_CONTENT;
    orglet->ud = c;
    orglet->linum = linum;
    worgle_orgfile_append(worg, f, orglet);
}
4.11.4.4. DONE Appending Block Reference

CLOSED: [2019-12-10 Tue 15:49] Appends a reference to a code block. Should be called when a block first starts.

<<function_declarations>>=
void worgle_orgfile_append_reference(worgle_d *worg,
                                     worgle_block *blk);
<<functions>>=
void worgle_orgfile_append_reference(worgle_d *worg,
                                     worgle_block *blk)
{
    worgle_orglet *orglet;
    worgle_orglet_blkref *br;
    worgle_orgfile *f;
    size_t linum;

    f = worg->curorg;
    linum = worg->linum;

    orglet = calloc(1, sizeof(worgle_orglet));
    br = calloc(1, sizeof(worgle_orglet_blkref));

<<orglet_blkref_setup>>
    worgle_orglet_init(orglet);

    orglet->type = ORGLET_BLKREF;
    orglet->ud = br;
    orglet->linum = linum;
    worgle_orgfile_append(worg, f, orglet);
}

4.12. Orglet

A worgle_orglet is a single entry inside of a worgle_orgfile.

4.12.1. Struct

<<typedefs>>=
typedef struct worgle_orglet worgle_orglet;
<<worgle_orglet>>=
<<worgle_orglet_subtypes>>
struct worgle_orglet {
    int type;
    void *ud;
    worgle_orglet *next;
    size_t linum;
<<worgle_orglet_id>>
};

4.12.2. Initializing

<<function_declarations>>=
void worgle_orglet_init(worgle_orglet *orglet);
<<functions>>=
void worgle_orglet_init(worgle_orglet *orglet)
{
    orglet->type = ORGLET_UNKNOWN;
    orglet->ud = NULL;
    orglet->next = NULL;
    orglet->linum = 0;
<<worgle_orglet_id_init>>
}

4.12.3. Freeing

<<function_declarations>>=
void worgle_orglet_free(worgle_orglet *orglet);
<<functions>>=
void worgle_orglet_free(worgle_orglet *orglet)
{
    free(orglet->ud);
    free(orglet);
}

4.12.4. Types

populated here

<<enums>>=
enum {
<<orglet_types>>
ORGLET_UNKNOWN
};

4.12.5. Orglet Header

<<orglet_types>>=
ORGLET_HEADER,
<<typedefs>>=
typedef struct worgle_orglet_header worgle_orglet_header;
<<worgle_orglet_subtypes>>=
struct worgle_orglet_header {
    worgle_string str;
    int lvl;
};

4.12.6. Orglet Content

<<orglet_types>>=
ORGLET_CONTENT,
<<typedefs>>=
typedef struct worgle_orglet_content worgle_orglet_content;
<<worgle_orglet_subtypes>>=
struct worgle_orglet_content {
    worgle_string text;
};

4.12.7. Orglet Block Reference

<<orglet_types>>=
ORGLET_BLKREF,
<<typedefs>>=
typedef struct worgle_orglet_blkref worgle_orglet_blkref;
<<worgle_orglet_subtypes>>=
struct worgle_orglet_blkref {
    worgle_block *blk;
    int pos;
    int prev_lastseg;
    int segoff;
};

The integer variables here take a snapshot of current code block, and can be used to recreate the code inside a code subblock.

pos is derived from the nblocks counter, and is turned into a relative position id.

prev_lastseg is the id of last segment of the previous block, and is negative if it doesn't exist. Peaking at the next id of this segment gets the first segment of the subblock.

segoff is the segment offset. It stores the starting segment position relative to the block. Subtracting the segoff of the next subblock obtains the number of segments in the subblock. If there is no next block, the offset can be subtracted from the total number of segments in the block to get number of segments in the subblock.

<<orglet_blkref_setup>>=
br->blk = blk;
br->pos = blk->nblocks;
br->prev_lastseg = blk->last_seg;
br->segoff = blk->nsegs;



prev | home | next