ugbase/lib_grid/file_io/externals/src/ng/src/ng_parser.c

Summary

Maintainability
Test Coverage
/* TODO: Use carray for dynamic arrays. */

#include "ng_parser.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "../../../include/tokstream/tokstream.h"

#include "../include/ng_node.h"
#include "../include/ng_element.h"
#include "ng_error.h"


/*
 * default sizes for array allocations
 * always counts per entry
 */
static const int init_num_bnodes = 128;
static const int init_num_inodes = 128;
static const int init_num_elements = 64;

static const int init_num_spos = 4;
static const int init_num_lpos = 4;

static const int init_num_nodes = 16;
static const int init_num_faces = 8;

/*
 * error messages
 */

static const char* err_msg_tok = "Could not read token at line %d, char %d.";
static const char* err_msg_BIE = "Expected 'B', 'I' or 'E' token at line %d, char %d.";
static const char* err_msg_mem = "Failed to allocate memory for NG data.";
static const char* err_msg_fil = "Error opening file \"%s\".";

static const char* err_msg_nob = "Not a bnode [internal error], line %d, char %d.";
static const char* err_msg_noi = "Not an inode [internal error], line %d, char %d.";
static const char* err_msg_coo = "Error reading coordinates at line %d, char %d.";
static const char* err_msg_SLS = "Expected 'S', 'L' or ';' token at line %d, char %d.";
static const char* err_msg_sem = "Expected ';' token at line %d, char %d.";

static const char* err_msg_nos = "Not a surface_pos [internal error], line %d, char %d.";
static const char* err_msg_sid = "Error reading surface id at line %d, char %d.";
static const char* err_msg_spo = "Error reading surface position at line %d, char %d.";

static const char* err_msg_nol = "Not a line_pos [internal error], line %d, char %d.";
static const char* err_msg_lid = "Error reading line id at line %d, char %d.";
static const char* err_msg_lpo = "Error reading line position at line %d, char %d.";

static const char* err_msg_noe = "Not an element [internal error], line %d, char %d.";
static const char* err_msg_uid = "Error reading subdomain id at line %d, char %d.";
static const char* err_msg_SNF = "Expected node id or 'F' token at line %d, char %d.";

static const char* err_msg_nof = "Not a face nor a side [internal error], line %d, char %d.";


/*
 * inlining macros
 */

/* try-like construct, if cmd returns 1 (error), return 1 too */
#define $(cmd) do { if(cmd) return 1; } while (0)

/* short for str != NULL && strcmp == 0 */
#define is_token(str, tok) (str != NULL && strcmp(str, tok) == 0)


/*
 * internal functions
 */

int ng_parser_strtoi(const char* str, int* i);
int ng_parser_strtod(const char* str, double* d);


/****
 * ng file parsing
 */

/* parsing functions */
int ng_parse_file(tokstream* ts, struct ng* n, struct ng_info* fileinfo);
int ng_parse_bnode(tokstream* ts, struct ng_bnode* bnode, struct ng* n, struct ng_info* fileinfo);
int ng_parse_inode(tokstream* ts, struct ng_inode* inode, struct ng* n, struct ng_info* fileinfo);
int ng_parse_element(tokstream* ts, struct ng_element* element, struct ng_info* fileinfo, int dim);
int ng_parse_surface_pos(tokstream* ts, struct ng_surface_pos* spos, struct ng_info* fileinfo);
int ng_parse_line_pos(tokstream* ts, struct ng_line_pos* lpos, struct ng_info* fileinfo);
int ng_parse_face(tokstream* ts, struct ng_face* face, struct ng_info* fileinfo);


int ng_parse(const char* file, struct ng* n, struct ng_info* fileinfo)
{
    /* open token stream */
    tokstream* ts = ts_open(file);
    if(!ts)
        return ng_error_string(fileinfo, err_msg_fil, file);

    /* set whitespace separators */
    ts_sep(ts, NG_SEP);

    /* set semicolon delimiter */
    ts_delim(ts, NG_DELIM);

    /* set buffer size */
    ts_bufsiz(ts, 8192);

    /* read file */
    $(ng_parse_file(ts, n, fileinfo));

    /* close token stream */
    ts_close(ts);

    /* success */
    return 0;
}

int ng_parse_file(tokstream* ts, struct ng* n, struct ng_info* fileinfo)
{
    const char* tok;

    int alloc_bnodes = init_num_bnodes;
    int alloc_inodes = init_num_inodes;
    int alloc_elements = init_num_elements;

    /* initialize bnodes */
    n->num_bnodes = 0;
    n->bnodes = malloc(sizeof(struct ng_bnode) * alloc_bnodes);
    if(!n->bnodes)
        return ng_error(fileinfo, err_msg_mem);

    /* scan bnodes */
    while((tok = ts_get(ts)))
    {
        /* skip over comments */
        if(tok[0] == '#')
        {
            ts_skipline(ts);
            continue;
        }

        /* check for bnode */
        if(is_token(tok, "B"))
        {
            /* check for reallocation */
            if(n->num_bnodes == alloc_bnodes)
            {
                alloc_bnodes *= 2;
                n->bnodes = realloc(n->bnodes, sizeof(struct ng_bnode) * alloc_bnodes);
                if(!n->bnodes)
                    return ng_error(fileinfo, err_msg_mem);
            }

            /* read bnode */
            $(ng_parse_bnode(ts, &n->bnodes[n->num_bnodes], n, fileinfo));
            ++n->num_bnodes;

            continue;
        }

        /* end bnodes */
        $(ts_unget(ts));
        break;
    }

    /* finalize bnodes */
    n->bnodes = realloc(n->bnodes, sizeof(struct ng_bnode) * n->num_bnodes);

    /* check for errors */
    if(!tok && !ts_eof(ts))
        return ng_error_parse(fileinfo, err_msg_tok, ts);

    /* initialize inodes */
    n->num_inodes = 0;
    n->inodes = malloc(sizeof(struct ng_inode) * alloc_inodes);
    if(!n->inodes)
        return ng_error(fileinfo, err_msg_mem);

    /* scan inodes */
    while((tok = ts_get(ts)))
    {
        /* skip over comments */
        if(tok[0] == '#')
        {
            ts_skipline(ts);
            continue;
        }

        /* check for inode */
        if(is_token(tok, "I"))
        {
            /* check for reallocation */
            if(n->num_inodes == alloc_inodes)
            {
                alloc_inodes *= 2;
                n->inodes = realloc(n->inodes, sizeof(struct ng_inode) * alloc_inodes);
                if(!n->inodes)
                    return ng_error(fileinfo, err_msg_mem);
            }

            /* read inode */
            $(ng_parse_inode(ts, &n->inodes[n->num_inodes], n, fileinfo));
            ++n->num_inodes;

            continue;
        }

        /* end inodes */
        $(ts_unget(ts));
        break;
    }

    /* finalize inodes */
    n->inodes = realloc(n->inodes, sizeof(struct ng_inode) * n->num_inodes);

    /* check for errors */
    if(!tok && !ts_eof(ts))
        return ng_error_parse(fileinfo, err_msg_tok, ts);

    /* initialize elements */
    n->num_elements = 0;
    n->elements = malloc(sizeof(struct ng_element) * alloc_elements);
    if(!n->elements)
        return ng_error(fileinfo, err_msg_mem);

    /* scan elements */
    while((tok = ts_get(ts)))
    {
        /* skip over comments */
        if(tok[0] == '#')
        {
            ts_skipline(ts);
            continue;
        }

        /* check for element */
        if(is_token(tok, "E"))
        {
            /* check for reallocation */
            if(n->num_elements == alloc_elements)
            {
                alloc_elements *= 2;
                n->elements = realloc(n->elements, sizeof(struct ng_element) * alloc_elements);
                if(!n->elements)
                    return ng_error(fileinfo, err_msg_mem);
            }

            /* read element */
            $(ng_parse_element(ts, &n->elements[n->num_elements], fileinfo, n->dim));
            ++n->num_elements;

            continue;
        }

        /* end elements */
        $(ts_unget(ts));
        break;
    }

    /* finalize elements */
    n->elements = realloc(n->elements, sizeof(struct ng_element) * n->num_elements);

    /* check for errors */
    if(!tok && !ts_eof(ts))
        return ng_error_parse(fileinfo, err_msg_tok, ts);

    /* make sure whole file was read */
    if(!ts_eof(ts))
        return ng_error_parse(fileinfo, err_msg_BIE, ts);

    /* success */
    return 0;
}

int ng_parse_bnode(tokstream* ts, struct ng_bnode* bnode,
                   struct ng* n, struct ng_info* fileinfo)
{
    const char* tok;
    int i;
    double coord;

    int alloc_spos = init_num_spos;
    int alloc_lpos = init_num_lpos;

    /* check that this is a bnode */
    tok = ts_tok(ts);
    if(!is_token(tok, "B"))
        return ng_error_parse(fileinfo, err_msg_nob, ts);

    /* read coordinates */
    for(i = 0; i < n->dim; ++i)
    {
        tok = ts_get(ts);
        if(ng_parser_strtod(tok, &coord))
            return ng_error_parse(fileinfo, err_msg_coo, ts);
        bnode->coords[i] = coord;
    }

    /* initialize surface and line positions */
    bnode->num_spos = 0;
    bnode->spos = malloc(sizeof(struct ng_surface_pos) * alloc_spos);
    if(!bnode->spos)
        return ng_error(fileinfo, err_msg_mem);

    /* initialize line positions */
    bnode->num_lpos = 0;
    bnode->lpos = malloc(sizeof(struct ng_line_pos) * alloc_lpos);
    if(!bnode->lpos)
        return ng_error(fileinfo, err_msg_mem);

    /* scan surface and line positions */
    while((tok = ts_get(ts)))
    {
        /* skip over comments */
        if(tok[0] == '#')
        {
            ts_skipline(ts);
            continue;
        }

        /* check for surface position */
        if(is_token(tok, "S"))
        {
            /* check for reallocation */
            if(bnode->num_spos == alloc_spos)
            {
                alloc_spos *= 2;
                bnode->spos = realloc(bnode->spos, sizeof(struct ng_surface_pos) * alloc_spos);
                if(!bnode->spos)
                    return ng_error(fileinfo, err_msg_mem);
            }

            /* read surface position */
            $(ng_parse_surface_pos(ts, &bnode->spos[bnode->num_spos], fileinfo));
            ++bnode->num_spos;

            continue;
        }

        /* check for line position */
        if(is_token(tok, "L"))
        {
            /* check for reallocation */
            if(bnode->num_lpos == alloc_lpos)
            {
                alloc_lpos *= 2;
                bnode->lpos = realloc(bnode->lpos, sizeof(struct ng_line_pos) * alloc_lpos);
                if(!bnode->lpos)
                    return ng_error(fileinfo, err_msg_mem);
            }

            /* read line position */
            $(ng_parse_line_pos(ts, &bnode->lpos[bnode->num_lpos], fileinfo));
            ++bnode->num_lpos;

            continue;
        }

        /* end surface and line positions */
        break;
    }

    /* finalize surface positions */
    bnode->spos = realloc(bnode->spos, sizeof(struct ng_surface_pos) * bnode->num_spos);

    /* finalize line positions */
    bnode->lpos = realloc(bnode->lpos, sizeof(struct ng_line_pos) * bnode->num_lpos);

    /* check for errors */
    if(!tok && !ts_eof(ts))
        return ng_error_parse(fileinfo, err_msg_tok, ts);

    /* read delimiter */
    tok = ts_tok(ts);
    if(!is_token(tok, ";"))
        return ng_error_parse(fileinfo, err_msg_SLS, ts);

    /* success */
    return 0;
}

int ng_parse_surface_pos(tokstream* ts, struct ng_surface_pos* spos, struct ng_info* fileinfo)
{
    const char* tok;
    int i;

    /* check that this is a surface_pos */
    tok = ts_tok(ts);
    if(!is_token(tok, "S"))
        return ng_error_parse(fileinfo, err_msg_nos, ts);

    /* read surface id */
    tok = ts_get(ts);
    if(ng_parser_strtoi(tok, &spos->surface))
        return ng_error_parse(fileinfo, err_msg_sid, ts);

    /* read surface pos */
    for(i = 0; i < 3; ++i)
    {
        tok = ts_get(ts);
        if(ng_parser_strtod(tok, &spos->pos[i]))
            return ng_error_parse(fileinfo, err_msg_spo, ts);
    }

    /* success */
    return 0;
}

int ng_parse_line_pos(tokstream* ts, struct ng_line_pos* lpos, struct ng_info* fileinfo)
{
    const char* tok;

    /* check that this is a line_pos */
    tok = ts_tok(ts);
    if(!is_token(tok, "L"))
        return ng_error_parse(fileinfo, err_msg_nol, ts);

    /* read line id */
    tok = ts_get(ts);
    if(ng_parser_strtoi(tok, &lpos->line))
        return ng_error_parse(fileinfo, err_msg_lid, ts);

    /* read line pos */
    tok = ts_get(ts);
    if(ng_parser_strtod(tok, &lpos->pos))
        return ng_error_parse(fileinfo, err_msg_lpo, ts);

    /* success */
    return 0;
}

int ng_parse_inode(tokstream* ts, struct ng_inode* bnode,
                   struct ng* n, struct ng_info* fileinfo)
{
    const char* tok;
    int i;

    /* check that this is an inode */
    tok = ts_tok(ts);
    if(!is_token(tok, "I"))
        return ng_error_parse(fileinfo, err_msg_noi, ts);

    /* read coordinates */
    for(i = 0; i < n->dim; ++i)
    {
        tok = ts_get(ts);
        if(ng_parser_strtod(tok, &bnode->coords[i]))
            return ng_error_parse(fileinfo, err_msg_coo, ts);
    }

    /* read delimiter */
    tok = ts_get(ts);
    if(!is_token(tok, ";"))
        return ng_error_parse(fileinfo, err_msg_sem, ts);

    /* success */
    return 0;
}

int ng_parse_element(tokstream* ts, struct ng_element* element, struct ng_info* fileinfo, int dim)
{
    const char* tok;
    int subdomain_id;
    int node_id;

    int alloc_nodes = init_num_nodes;
    int alloc_faces = init_num_faces;

    /* check that this is an element */
    tok = ts_tok(ts);
    if(!is_token(tok, "E"))
        return ng_error_parse(fileinfo, err_msg_noe, ts);

    /* read subdomain id */
    tok = ts_get(ts);
    if(ng_parser_strtoi(tok, &subdomain_id))
        return ng_error_parse(fileinfo, err_msg_uid, ts);
    element->subdomain = subdomain_id;

    /* initialize nodes */
    element->num_nodes = 0;
    element->nodes = malloc(sizeof(int) * alloc_nodes);
    if(!element->nodes)
        return ng_error(fileinfo, err_msg_mem);

    /* scan nodes */
    while((tok = ts_get(ts)))
    {
        /* skip over comments */
        if(tok[0] == '#')
        {
            ts_skipline(ts);
            continue;
        }

        /* check for node id */
        if(!ng_parser_strtoi(tok, &node_id))
        {
            /* check for reallocation */
            if(element->num_nodes == alloc_nodes)
            {
                alloc_nodes *= 2;
                element->nodes = realloc(element->nodes, sizeof(int) * alloc_nodes);
                if(!element->nodes)
                    return ng_error(fileinfo, err_msg_mem);
            }

            /* store node id */
            element->nodes[element->num_nodes] = node_id;
            ++element->num_nodes;

            continue;
        }

        /* or end nodes */
        ts_unget(ts);
        break;
    }

    /* finalize nodes */
    element->nodes = realloc(element->nodes, sizeof(int) * element->num_nodes);

    /* check for errors */
    if(!tok && !ts_eof(ts))
        return ng_error_parse(fileinfo, err_msg_tok, ts);

    /* initialize faces */
    element->num_faces = 0;
    element->faces = malloc(sizeof(struct ng_face) * alloc_faces);
    if(!element->faces)
        return ng_error(fileinfo, err_msg_mem);

    /* scan faces */
    while((tok = ts_get(ts)))
    {
        /* skip over comments */
        if(tok[0] == '#')
        {
            ts_skipline(ts);
            continue;
        }

        /* check for face */
        if(is_token(tok, "F") || is_token(tok, "S"))
        {
            /* check for reallocation */
            if(element->num_faces == alloc_faces)
            {
                alloc_faces *= 2;
                element->faces = realloc(element->faces, sizeof(struct ng_face) * alloc_faces);
                if(!element->faces)
                    return ng_error(fileinfo, err_msg_mem);
            }

            /* read face */
            $(ng_parse_face(ts, &element->faces[element->num_faces], fileinfo));
            ++element->num_faces;

            continue;
        }

        /* or end faces */
        ts_unget(ts);
        break;
    }

    /* finalize faces */
    element->faces = realloc(element->faces, sizeof(struct ng_face) * element->num_faces);

    /* check for errors */
    if(!tok && !ts_eof(ts))
        return ng_error_parse(fileinfo, err_msg_tok, ts);

    /* read delimiter */
    tok = ts_get(ts);
    if(!is_token(tok, ";"))
        return ng_error_parse(fileinfo, err_msg_SNF, ts);

    /* success */
    return 0;
}

int ng_parse_face(tokstream* ts, struct ng_face* face, struct ng_info* fileinfo)
{
    const char* tok;
    int node_id;

    int alloc_nodes = init_num_nodes;

    /* check that this is a face or a side*/
    tok = ts_tok(ts);
    if((!is_token(tok, "F")) && (!is_token(tok, "S")))
        return ng_error_parse(fileinfo, err_msg_nof, ts);

    /* initialize nodes */
    face->num_nodes = 0;
    face->nodes = malloc(sizeof(int) * alloc_nodes);
    if(!face->nodes)
        return ng_error(fileinfo, err_msg_mem);

    /* scan nodes */
    while((tok = ts_get(ts)))
    {
        /* skip over comments */
        if(tok[0] == '#')
        {
            ts_skipline(ts);
            continue;
        }

        /* check for node id */
        if(!ng_parser_strtoi(tok, &node_id))
        {
            /* check for reallocation */
            if(face->num_nodes == alloc_nodes)
            {
                alloc_nodes *= 2;
                face->nodes = realloc(face->nodes, sizeof(int) * alloc_nodes);
                if(!face->nodes)
                    return ng_error(fileinfo, err_msg_mem);
            }

            /* store node id */
            face->nodes[face->num_nodes] = node_id;
            ++face->num_nodes;

            continue;
        }

        /* or end face */
        ts_unget(ts);
        break;
    }

    /* finalize nodes */
    face->nodes = realloc(face->nodes, sizeof(int) * face->num_nodes);

    /* check for errors */
    if(!tok && !ts_eof(ts))
        return ng_error_parse(fileinfo, err_msg_tok, ts);

    /* success */
    return 0;
}


/*
 * internal implementation
 */

int ng_parser_strtoi(const char* str, int* i)
{
    long int ii;
    char* endptr;

    /* convert */
    ii = strtol(str, &endptr, 10);

    /* check that nothing remains in string */
    if(*endptr)
        return 1;

    /* copy value */
    *i = (int)ii;

    /* success */
    return 0;
}

int ng_parser_strtod(const char* str, double* d)
{
    double dd;
    char* endptr;

    /* convert */
    dd = strtod(str, &endptr);

    /* check that nothing remains in string */
    if(*endptr)
        return 1;

    /* copy value */
    *d = dd;

    /* success */
    return 0;
}