mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
302 lines
7.9 KiB
C
302 lines
7.9 KiB
C
// Copyright (c) 2019 Christoffer Lerno. All rights reserved.
|
|
// Use of this source code is governed by the GNU LGPLv3.0 license
|
|
// a copy of which can be found in the LICENSE file.
|
|
|
|
#include "compiler_internal.h"
|
|
|
|
#define TABLE_MAX_LOAD 0.75
|
|
#define MAX_HASH_SIZE (1024 * 1024)
|
|
|
|
|
|
typedef struct _SymEntry
|
|
{
|
|
const char *value;
|
|
TokenType type;
|
|
uint32_t key_len;
|
|
uint32_t hash;
|
|
} SymEntry;
|
|
|
|
typedef struct _SymTab
|
|
{
|
|
uint32_t count;
|
|
uint32_t capacity;
|
|
SymEntry *entries;
|
|
} SymTab;
|
|
|
|
typedef struct _Entry
|
|
{
|
|
const char *key;
|
|
uint32_t key_len;
|
|
uint32_t hash;
|
|
void *value;
|
|
} Entry;
|
|
|
|
|
|
static SymTab symtab;
|
|
|
|
const char *attribute_list[NUMBER_OF_ATTRIBUTES];
|
|
|
|
const char *kw_align;
|
|
const char *kw_deprecated;
|
|
const char *kw_distinct;
|
|
const char *kw_ensure;
|
|
const char *kw_elements;
|
|
const char *kw_errors;
|
|
const char *kw_inf;
|
|
const char *kw_inline;
|
|
const char *kw_iterator;
|
|
const char *kw_len;
|
|
const char *kw_main;
|
|
const char *kw_max;
|
|
const char *kw_min;
|
|
const char *kw_next;
|
|
const char *kw_nan;
|
|
const char *kw_ordinal;
|
|
const char *kw_param;
|
|
const char *kw_pure;
|
|
const char *kw_reqparse;
|
|
const char *kw_require;
|
|
const char *kw_std;
|
|
const char *kw___ceil;
|
|
const char *kw___round;
|
|
const char *kw___sqrt;
|
|
const char *kw___trunc;
|
|
const char *kw_FILE;
|
|
const char *kw_FUNC;
|
|
const char *kw_LINE;
|
|
const char *kw_LINEREAL;
|
|
const char *kw_default_iterator;
|
|
const char *kw_incr;
|
|
const char *kw_check_assign;
|
|
|
|
void symtab_init(uint32_t capacity)
|
|
{
|
|
assert (is_power_of_two(capacity) && "Must be a power of two");
|
|
if (symtab.capacity != 0)
|
|
{
|
|
free(symtab.entries);
|
|
}
|
|
size_t size = capacity *sizeof(SymEntry);
|
|
symtab.entries = MALLOC(size);
|
|
memset(symtab.entries, 0, size);
|
|
symtab.count = 0;
|
|
symtab.capacity = capacity;
|
|
|
|
// Add keywords.
|
|
for (int i = 0; i < TOKEN_LAST; i++)
|
|
{
|
|
const char* name = token_type_to_string(i);
|
|
// Skip non-keywords
|
|
if (!is_lower(name[0]))
|
|
{
|
|
if ((name[0] != '@' && name[0] != '$') || !is_lower(name[1])) continue;
|
|
}
|
|
uint32_t len = (uint32_t)strlen(name);
|
|
TokenType type = (TokenType)i;
|
|
const char* interned = symtab_add(name, (uint32_t)strlen(name), fnv1a(name, len), &type);
|
|
assert(type == (TokenType)i);
|
|
assert(symtab_add(name, (uint32_t)strlen(name), fnv1a(name, len), &type) == interned);
|
|
}
|
|
|
|
// Init some constant idents
|
|
TokenType type = TOKEN_IDENT;
|
|
#define KW_DEF(x) symtab_add(x, sizeof(x) - 1, fnv1a(x, sizeof(x) - 1), &type)
|
|
kw_align = KW_DEF("align");
|
|
kw_deprecated = KW_DEF("deprecated");
|
|
kw_distinct = KW_DEF("distinct");
|
|
kw_elements = KW_DEF("elements");
|
|
kw_ensure = KW_DEF("ensure");
|
|
kw_errors = KW_DEF("errors");
|
|
kw_inf = KW_DEF("inf");
|
|
kw_inline = KW_DEF("inline");
|
|
kw_iterator = KW_DEF("iterator");
|
|
kw_len = KW_DEF("len");
|
|
kw_main = KW_DEF("main");
|
|
kw_max = KW_DEF("max");
|
|
kw_min = KW_DEF("min");
|
|
kw_nan = KW_DEF("nan");
|
|
kw_next = KW_DEF("next");
|
|
kw_ordinal = KW_DEF("ordinal");
|
|
kw_param = KW_DEF("param");
|
|
kw_pure = KW_DEF("pure");
|
|
kw_require = KW_DEF("require");
|
|
kw_std = KW_DEF("std");
|
|
kw___ceil = KW_DEF("__ceil");
|
|
kw___round = KW_DEF("__round");
|
|
kw___sqrt = KW_DEF("__sqrt");
|
|
kw___trunc = KW_DEF("__trunc");
|
|
kw_LINE = KW_DEF("LINE");
|
|
kw_LINEREAL = KW_DEF("LINEREAL");
|
|
kw_FILE = KW_DEF("FILE");
|
|
kw_FUNC = KW_DEF("FUNC");
|
|
kw_incr = KW_DEF("incr");
|
|
kw_default_iterator = KW_DEF("default_iterator");
|
|
kw_check_assign = KW_DEF("check_assign");
|
|
attribute_list[ATTRIBUTE_INLINE] = kw_inline;
|
|
attribute_list[ATTRIBUTE_NOINLINE] = KW_DEF("noinline");
|
|
attribute_list[ATTRIBUTE_OPAQUE] = KW_DEF("opaque");
|
|
attribute_list[ATTRIBUTE_NORETURN] = KW_DEF("noreturn");
|
|
attribute_list[ATTRIBUTE_SECTION] = KW_DEF("section");
|
|
attribute_list[ATTRIBUTE_EXTNAME] = KW_DEF("extname");
|
|
attribute_list[ATTRIBUTE_WEAK] = KW_DEF("weak");
|
|
attribute_list[ATTRIBUTE_ALIGN] = kw_align;
|
|
attribute_list[ATTRIBUTE_PACKED] = KW_DEF("packed");
|
|
attribute_list[ATTRIBUTE_UNUSED] = KW_DEF("unused");
|
|
attribute_list[ATTRIBUTE_USED] = KW_DEF("used");
|
|
attribute_list[ATTRIBUTE_NAKED] = KW_DEF("naked");
|
|
attribute_list[ATTRIBUTE_CDECL] = KW_DEF("cdecl");
|
|
attribute_list[ATTRIBUTE_STDCALL] = KW_DEF("stdcall");
|
|
attribute_list[ATTRIBUTE_VECCALL] = KW_DEF("veccall");
|
|
attribute_list[ATTRIBUTE_REGCALL] = KW_DEF("regcall");
|
|
attribute_list[ATTRIBUTE_FASTCALL] = KW_DEF("fastcall");
|
|
attribute_list[ATTRIBUTE_OVERLAP] = KW_DEF("overlap");
|
|
}
|
|
|
|
static inline SymEntry *entry_find(const char *key, uint32_t key_len, uint32_t hash)
|
|
{
|
|
uint32_t index = hash & (symtab.capacity - 1);
|
|
while (1)
|
|
{
|
|
SymEntry *entry = &symtab.entries[index];
|
|
if (entry->key_len == key_len && (entry->value == key || memcmp(key, entry->value, key_len) == 0)) return entry;
|
|
if (entry->value == NULL)
|
|
{
|
|
return entry;
|
|
}
|
|
index = (index + 1) % (symtab.capacity - 1);
|
|
}
|
|
}
|
|
|
|
const char *symtab_add(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type)
|
|
{
|
|
if (symtab.count + 1 > symtab.capacity * TABLE_MAX_LOAD)
|
|
{
|
|
FATAL_ERROR("Symtab exceeded capacity, please increase --symtab.");
|
|
}
|
|
SymEntry *entry = entry_find(symbol, len, fnv1hash);
|
|
if (entry->value)
|
|
{
|
|
*type = entry->type;
|
|
return entry->value;
|
|
}
|
|
|
|
char *copy = MALLOC(len + 1);
|
|
memcpy(copy, symbol, len);
|
|
copy[len] = '\0';
|
|
entry->value = copy;
|
|
entry->key_len = len;
|
|
entry->hash = fnv1hash;
|
|
entry->type = *type;
|
|
symtab.count++;
|
|
return entry->value;
|
|
}
|
|
|
|
const char *symtab_find(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type)
|
|
{
|
|
SymEntry *entry = entry_find(symbol, len, fnv1hash);
|
|
if (!entry->value) return NULL;
|
|
*type = entry->type;
|
|
return entry->value;
|
|
}
|
|
|
|
void stable_init(STable *table, uint32_t initial_size)
|
|
{
|
|
assert(initial_size && "Size must be larger than 0");
|
|
assert (is_power_of_two(initial_size) && "Must be a power of two");
|
|
|
|
SEntry *entries = MALLOC(initial_size * sizeof(Entry));
|
|
for (uint32_t i = 0; i < initial_size; i++)
|
|
{
|
|
entries[i].key = NULL;
|
|
entries[i].value = NULL;
|
|
}
|
|
table->count = 0;
|
|
table->capacity = initial_size;
|
|
table->entries = entries;
|
|
}
|
|
|
|
void stable_clear(STable *table)
|
|
{
|
|
memset(table->entries, 0, table->capacity * sizeof(Entry));
|
|
table->count = 0;
|
|
}
|
|
|
|
#define TOMBSTONE ((void *)0x01)
|
|
static SEntry *sentry_find(SEntry *entries, uint32_t capacity, const char *key)
|
|
{
|
|
uint32_t index = (uint32_t)((((uintptr_t)key) >> 2u) & (capacity - 1));
|
|
SEntry *tombstone = NULL;
|
|
while (1)
|
|
{
|
|
SEntry *entry = &entries[index];
|
|
if (entry->key == key) return entry;
|
|
if (entry->key == NULL)
|
|
{
|
|
if (entry->value != TOMBSTONE)
|
|
{
|
|
return tombstone ? tombstone : entry;
|
|
}
|
|
else
|
|
{
|
|
if (!tombstone) tombstone = entry;
|
|
}
|
|
}
|
|
index = (index + 1) & (capacity - 1);
|
|
}
|
|
}
|
|
|
|
|
|
void *stable_set(STable *table, const char *key, void *value)
|
|
{
|
|
assert(value && "Cannot insert NULL");
|
|
if (table->count + 1 > table->capacity * TABLE_MAX_LOAD)
|
|
{
|
|
ASSERT(table->capacity < MAX_HASH_SIZE, "Table size too large, exceeded %d", MAX_HASH_SIZE);
|
|
|
|
uint32_t new_capacity = table->capacity ? (table->capacity << 1u) : 16u;
|
|
SEntry *new_data = MALLOC(new_capacity * sizeof(SEntry));
|
|
for (uint32_t i = 0; i < new_capacity; i++)
|
|
{
|
|
new_data[i].key = NULL;
|
|
new_data[i].value = NULL;
|
|
}
|
|
table->count = 0;
|
|
for (uint32_t i = 0; i < table->capacity; i++)
|
|
{
|
|
SEntry *entry = &table->entries[i];
|
|
if (!entry->key) continue;
|
|
table->count++;
|
|
SEntry *dest = sentry_find(new_data, new_capacity, entry->key);
|
|
*dest = *entry;
|
|
}
|
|
table->entries = new_data;
|
|
table->capacity = new_capacity;
|
|
}
|
|
|
|
SEntry *entry = sentry_find(table->entries, table->capacity, key);
|
|
void *old = entry->value && entry->value != TOMBSTONE ? entry->value : NULL;
|
|
entry->key = key;
|
|
entry->value = value;
|
|
if (!old) table->count++;
|
|
return old;
|
|
}
|
|
|
|
|
|
void *stable_get(STable *table, const char *key)
|
|
{
|
|
if (!table->entries) return NULL;
|
|
SEntry *entry = sentry_find(table->entries, table->capacity, key);
|
|
return entry->key == NULL ? NULL : entry->value;
|
|
}
|
|
|
|
void *stable_delete(STable *table, const char *key)
|
|
{
|
|
if (!table->count) return NULL;
|
|
SEntry *entry = sentry_find(table->entries, table->capacity, key);
|
|
if (!entry->key) return NULL;
|
|
void *value = entry->value;
|
|
entry->key = NULL;
|
|
entry->value = TOMBSTONE;
|
|
return value;
|
|
}
|