/* @@@HEAD@@@
// Miscellaneous operations.
*/

#include "config.h"

#include <stdio.h>
#include <stdlib.h>
#include "defs.h"
#include "y.tab.h"
#include "cdc_types.h"
#include "execute.h"

/* hardcoded wordsize, bad bad, but faster than mallocing it */
#define WORDSIZE 255

#define add_as_ident(__i) { \
        *word = __i, word++, *word = NULL; \
        d.type = SYMBOL; \
        d.u.symbol = ident_get(w); \
        list = list_add(list, &d); \
        w[0] = NULL; \
        word = w; \
        ident_discard(d.u.symbol); \
    }

#define add_as_str(__w) { \
        if (wlen) { \
            sw = string_from_chars(__w, wlen); \
            d.type = STRING; \
            d.u.str = sw; \
            list = list_add(list, &d); \
            string_discard(sw); \
            __w[0] = NULL; \
            word = __w; \
            wlen = 0; \
        } \
    }

list_t * tokenize_cml(string_t * str) {
    /* not that registering these will make much of a diff */
    register int   escaped;
    int            wlen;
    char         * s,
                   w[WORDSIZE],
                 * word;
    list_t       * list;
    data_t           d;
    string_t     * sw;

    /* initialize everything */
    escaped = 0;
    s = string_chars(str);
    w[0] = NULL;
    word = w;
    wlen = 0;
    list = list_new(0);

    for (; *s != NULL; s++) {
        if (escaped) {
            escaped--;

            /* break the word, its all we can do with a restricted buffer */
            if ((wlen + 1) == (WORDSIZE - 1))
                add_as_str(w);

            wlen++, *word = *s, word++, *word = NULL;
        } else {
            switch (*s) {
                case '\\':
                    escaped = 1;
                    break;
                case ' ':
                    add_as_str(w)
                    break;
                case '{':
                case '}':
                case '[':
                case ']':
                case '=':
                case '"':
                case ':':
                    add_as_str(w);
                    add_as_ident(*s);
                    break;
                default:
                    wlen++, *word = *s, word++, *word = NULL;
                    break;
            }
        }
    }

    if (wlen)
        add_as_str(w);

    return list;
}

void op_tokenize_cml(void) {
    data_t         * args,
                 * elem;
    list_t       * list;
    string_t     * str;

    if (!func_init_1(&args, LIST))
        return;

    list = args[0].u.list;

    str = string_new(0);

    /* Join the list into a single string, denote line breaks */
    for (elem = list_first(list); elem; elem = list_next(list, elem)) {
        if (elem->type != STRING) {
            string_discard(str);
            cthrow(type_id, "Line %d (%D) is not a string.",
                   elem - list_first(list), elem);
            return;
        }
        str = string_add_chars(str, elem->u.str->s, elem->u.str->len);
        str = string_add_chars(str, "{_ln}", 5);
    }

    /* the old list was just a pointer to args, which gets popped */
    list = tokenize_cml(str);

    pop(1);
    push_list(list);
    list_discard(list);
}