<commit_before>#include "input.h"
#include "macro.h"
#include "strtab.h"
#include "tokenize.h"
#include <lacc/cli.h>
#include <lacc/hash.h>
#include <lacc/list.h>

#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>

#define HASH_TABLE_BUCKETS 1024

static struct hash_table macro_hash_table;
static int initialized;
static int new_macro_added;

static int macrocmp(const struct macro *a, const struct macro *b)
{
    int i;
    struct replacement ra, rb;

    if ((a->type != b->type) || (a->params != b->params))
        return 1;

    if (tok_cmp(a->name, b->name))
        return 1;

    if (a->size != b->size)
        return 1;

    for (i = 0; i < a->size; ++i) {
        ra = a->replacement[i];
        rb = b->replacement[i];

        if ((ra.param || rb.param) && (ra.param != rb.param))
            return 1;

        if (tok_cmp(ra.token, rb.token))
            return 1;
    }

    return 0;
}

static struct string macro_hash_key(void *ref)
{
    return ((struct macro *) ref)->name.d.string;
}

static void macro_hash_del(void *ref)
{
    struct macro *macro = (struct macro *) ref;
    if (macro->replacement)
        free(macro->replacement);
    free(macro);
}

static void *macro_hash_add(void *ref)
{
    struct macro *macro, *arg;

    arg = (struct macro *) ref;
    macro = calloc(1, sizeof(*macro));
    *macro = *arg;

    /* Signal that the hash table has ownership now, and it will not be
     * freed in define(). */
    new_macro_added = 1;
    return macro;
}

static void cleanup(void)
{
    hash_destroy(&macro_hash_table);
}

static void initialize(void)
{
    assert(!initialized);
    hash_init(
        &macro_hash_table,
        HASH_TABLE_BUCKETS,
        macro_hash_key,
        macro_hash_add,
        macro_hash_del);
    atexit(cleanup);
    initialized = 1;
}

const struct macro *definition(struct token name)
{
    struct macro *ref = NULL;

    if (!initialized)
        initialize();

    if (name.token == IDENTIFIER) {
        ref = hash_lookup(&macro_hash_table, name.d.string);
        if (ref) {
            /* Replace __LINE__ with current line number, by mutating
             * the replacement list on the fly. */
            if (!str_cmp(ref->name.d.string, str_init("__LINE__")))
                ref->replacement[0].token.d.number.val.i = current_file.line;
        }
    }

    return ref;
}

void define(struct macro macro)
{
    struct macro *ref;

    if (!initialized)
        initialize();

    new_macro_added = 0;
    ref = hash_insert(&macro_hash_table, &macro);
    if (macrocmp(ref, &macro)) {
        error("Redefinition of macro '%s' with different substitution.",
            macro.name.d.string.str);
        exit(1);
    }

    /* Need to clean up memory for replacement list since ownership was
     * not given to hash table. */
    if (!new_macro_added && macro.replacement)
        free(macro.replacement);
}

void undef(struct token name)
{
    if (!initialized)
        initialize();

    if (name.token == IDENTIFIER)
        hash_remove(&macro_hash_table, name.d.string);
}

/* Keep track of which macros have been expanded, avoiding recursion by
 * looking up in this list for each new expansion.
 */
static struct list expand_stack;<commit_msg>Initialize macro hash table more elegantly<commit_after>#include "input.h"
#include "macro.h"
#include "strtab.h"
#include "tokenize.h"
#include <lacc/cli.h>
#include <lacc/hash.h>
#include <lacc/list.h>

#include <assert.h>
#include <ctype.h>
#include <string.h>
#include <stddef.h>
#include <stdlib.h>

#define HASH_TABLE_BUCKETS 1024

static struct hash_table macro_hash_table;
static int new_macro_added;

static int macrocmp(const struct macro *a, const struct macro *b)
{
    int i;
    struct replacement ra, rb;

    if ((a->type != b->type) || (a->params != b->params))
        return 1;

    if (tok_cmp(a->name, b->name))
        return 1;

    if (a->size != b->size)
        return 1;

    for (i = 0; i < a->size; ++i) {
        ra = a->replacement[i];
        rb = b->replacement[i];

        if ((ra.param || rb.param) && (ra.param != rb.param))
            return 1;

        if (tok_cmp(ra.token, rb.token))
            return 1;
    }

    return 0;
}

static struct string macro_hash_key(void *ref)
{
    return ((struct macro *) ref)->name.d.string;
}

static void macro_hash_del(void *ref)
{
    struct macro *macro = (struct macro *) ref;
    if (macro->replacement)
        free(macro->replacement);
    free(macro);
}

static void *macro_hash_add(void *ref)
{
    struct macro *macro, *arg;

    arg = (struct macro *) ref;
    macro = calloc(1, sizeof(*macro));
    *macro = *arg;

    /* Signal that the hash table has ownership now, and it will not be
     * freed in define(). */
    new_macro_added = 1;
    return macro;
}

static void cleanup(void)
{
    hash_destroy(&macro_hash_table);
}

static void ensure_initialized(void)
{
    static int done;

    if (!done) {
        hash_init(
            &macro_hash_table,
            HASH_TABLE_BUCKETS,
            macro_hash_key,
            macro_hash_add,
            macro_hash_del);
        atexit(cleanup);
        done = 1;
    }
}

const struct macro *definition(struct token name)
{
    struct macro *ref = NULL;

    ensure_initialized();
    if (name.token == IDENTIFIER) {
        ref = hash_lookup(&macro_hash_table, name.d.string);
        if (ref) {
            /* Replace __LINE__ with current line number, by mutating
             * the replacement list on the fly. */
            if (!str_cmp(ref->name.d.string, str_init("__LINE__")))
                ref->replacement[0].token.d.number.val.i = current_file.line;
        }
    }

    return ref;
}

void define(struct macro macro)
{
    struct macro *ref;

    ensure_initialized();
    new_macro_added = 0;
    ref = hash_insert(&macro_hash_table, &macro);
    if (macrocmp(ref, &macro)) {
        error("Redefinition of macro '%s' with different substitution.",
            macro.name.d.string.str);
        exit(1);
    }

    /* Need to clean up memory for replacement list since ownership was
     * not given to hash table. */
    if (!new_macro_added && macro.replacement) {
        free(macro.replacement);
    }
}

void undef(struct token name)
{
    ensure_initialized();
    if (name.token == IDENTIFIER) {
        hash_remove(&macro_hash_table, name.d.string);
    }
}

/* Keep track of which macros have been expanded, avoiding recursion by
 * looking up in this list for each new expansion.
 */
static struct list expand_stack;
{'fraction_non_alphanumeric': 0.12827544180377817, 'fraction_numerical': 0.005179768433881779, 'mean_word_length': 2.577656675749319, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 51, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2850320', 'n_tokens_mistral': 2244, 'n_tokens_neox': 2132, 'n_words': 591}