mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
530 lines
11 KiB
C
530 lines
11 KiB
C
#pragma once
|
|
|
|
// 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 "common.h"
|
|
#include "setjmp.h"
|
|
|
|
#if PLATFORM_WINDOWS
|
|
#include "direct.h"
|
|
#endif
|
|
|
|
#define COMPILER_SUCCESS_EXIT -1000
|
|
extern jmp_buf on_err_jump;
|
|
|
|
extern bool debug_log;
|
|
extern bool debug_stats;
|
|
|
|
NORETURN void exit_compiler(int exit_value);
|
|
|
|
typedef struct Task_
|
|
{
|
|
void (*task)(void *arg);
|
|
void *arg;
|
|
} Task;
|
|
|
|
typedef void *TaskQueueRef;
|
|
|
|
bool filenamesplit(const char *path, char** filename_ptr, char** directory_ptr);
|
|
const char* expand_path(const char* path);
|
|
const char* find_lib_dir(void);
|
|
char *read_file(const char *path, size_t *return_size);
|
|
void path_get_dir_and_filename_from_full(const char *full_path, char **filename, char **dir_path);
|
|
void file_find_top_dir();
|
|
void file_add_wildcard_files(const char ***files, const char *path, bool recursive, const char *suffix1, const char *suffix2);
|
|
void *cmalloc(size_t size);
|
|
void *ccalloc(size_t size, size_t elements);
|
|
void memory_init(void);
|
|
void memory_release();
|
|
void *calloc_arena(size_t mem);
|
|
#define malloc_arena calloc_arena
|
|
void free_arena(void);
|
|
void print_arena_status(void);
|
|
void run_arena_allocator_tests(void);
|
|
TaskQueueRef taskqueue_create(int threads);
|
|
void taskqueue_add(TaskQueueRef queue, Task *task);
|
|
void taskqueue_destroy(TaskQueueRef queue);
|
|
void taskqueue_wait_for_completion(TaskQueueRef queue);
|
|
|
|
#define MALLOC(mem) malloc_arena(mem)
|
|
#define MALLOCS(type) malloc_arena(sizeof(type))
|
|
#define CALLOC(mem) calloc_arena(mem)
|
|
#define CALLOCS(type) calloc_arena(sizeof(type))
|
|
|
|
static inline bool is_power_of_two(uint64_t x)
|
|
{
|
|
return x != 0 && (x & (x - 1)) == 0;
|
|
}
|
|
|
|
static inline uint32_t next_highest_power_of_2(uint32_t v)
|
|
{
|
|
v--;
|
|
v |= v >> 1U;
|
|
v |= v >> 2U;
|
|
v |= v >> 4U;
|
|
v |= v >> 8U;
|
|
v |= v >> 16U;
|
|
v++;
|
|
return v;
|
|
}
|
|
|
|
|
|
|
|
static inline bool is_lower(char c)
|
|
{
|
|
return c >= 'a' && c <= 'z';
|
|
}
|
|
|
|
static inline bool is_lower_(char c)
|
|
{
|
|
return c == '_' || (c >= 'a' && c <= 'z');
|
|
}
|
|
|
|
static inline bool is_upper(char c)
|
|
{
|
|
return c >= 'A' && c <= 'Z';
|
|
}
|
|
|
|
static inline bool is_oct(char c)
|
|
{
|
|
return c >= '0' && c <= '7';
|
|
}
|
|
|
|
static inline bool is_oct_or_(char c)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '_':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline bool is_binary(char c)
|
|
{
|
|
return c == '0' || c == '1';
|
|
}
|
|
|
|
|
|
static inline bool is_binary_or_(char c)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '0': case '1': case '_':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline bool is_digit_or_(char c)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
case '_':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline bool is_digit(char c)
|
|
{
|
|
return c >= '0' && c <= '9';
|
|
}
|
|
|
|
/**
|
|
* Convert hex character to nibble
|
|
* @param c
|
|
* @return value or -1 if invalid.
|
|
*/
|
|
static inline int char_to_nibble(char c)
|
|
{
|
|
char conv[256] = {
|
|
['0'] = 1,
|
|
['1'] = 2,
|
|
['2'] = 3,
|
|
['3'] = 4,
|
|
['4'] = 5,
|
|
['5'] = 6,
|
|
['6'] = 7,
|
|
['7'] = 8,
|
|
['8'] = 9,
|
|
['9'] = 10,
|
|
['A'] = 11,
|
|
['B'] = 12,
|
|
['C'] = 13,
|
|
['D'] = 14,
|
|
['E'] = 15,
|
|
['F'] = 16,
|
|
['a'] = 11,
|
|
['b'] = 12,
|
|
['c'] = 13,
|
|
['d'] = 14,
|
|
['e'] = 15,
|
|
['f'] = 16,
|
|
};
|
|
return conv[(unsigned)c] - 1;
|
|
}
|
|
|
|
static inline bool is_hex_or_(char c)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'a': case 'b': case 'c': case 'd': case 'e':
|
|
case 'f':
|
|
case 'A': case 'B': case 'C': case 'D': case 'E':
|
|
case 'F':
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
case '_':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline signed char is_valid_escape(char c)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'a':
|
|
return '\a';
|
|
case 'b':
|
|
return '\b';
|
|
case 'e':
|
|
return 0x1B;
|
|
case 'f':
|
|
return '\f';
|
|
case 'n':
|
|
return '\n';
|
|
case 'r':
|
|
return '\r';
|
|
case 't':
|
|
return '\t';
|
|
case 'v':
|
|
return '\v';
|
|
case 'x':
|
|
return 'x';
|
|
case 'u':
|
|
return 'u';
|
|
case 'U':
|
|
return 'U';
|
|
case '\'':
|
|
return '\'';
|
|
case '"':
|
|
return '"';
|
|
case '\\':
|
|
return '\\';
|
|
case '0':
|
|
return '\0';
|
|
case 's':
|
|
return ' ';
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
static inline bool is_base64(char c)
|
|
{
|
|
return (c >= 'A' && c <= 'Z')
|
|
|| (c >= 'a' && c <= 'z')
|
|
|| (c >= '0' && c <= '9')
|
|
|| c == '+' || c == '/';
|
|
}
|
|
|
|
static inline bool is_hex(char c)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'a': case 'b': case 'c': case 'd': case 'e':
|
|
case 'f':
|
|
case 'A': case 'B': case 'C': case 'D': case 'E':
|
|
case 'F':
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline int hex_nibble(char c)
|
|
{
|
|
static int conv[256] = {
|
|
['0'] = 0, ['1'] = 1, ['2'] = 2, ['3'] = 3, ['4'] = 4,
|
|
['5'] = 5, ['6'] = 6, ['7'] = 7, ['8'] = 8, ['9'] = 9,
|
|
['A'] = 10, ['B'] = 11, ['C'] = 12, ['D'] = 13, ['E'] = 14, ['F'] = 15,
|
|
['a'] = 10, ['b'] = 11, ['c'] = 12, ['d'] = 13, ['e'] = 14, ['f'] = 15,
|
|
};
|
|
return conv[(unsigned char)c];
|
|
}
|
|
|
|
static inline char nibble_hex(int c)
|
|
{
|
|
static const char *conv = "0123456789ABCDEF";
|
|
return conv[c];
|
|
}
|
|
|
|
static inline bool is_whitespace(char c)
|
|
{
|
|
switch (c)
|
|
{
|
|
case ' ':
|
|
case '\t':
|
|
case '\n':
|
|
return true;
|
|
case '\r':
|
|
UNREACHABLE
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline bool is_alphanum_(char c)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'a': case 'b': case 'c': case 'd': case 'e':
|
|
case 'f': case 'g': case 'h': case 'i': case 'j':
|
|
case 'k': case 'l': case 'm': case 'n': case 'o':
|
|
case 'p': case 'q': case 'r': case 's': case 't':
|
|
case 'u': case 'v': case 'w': case 'x': case 'y':
|
|
case 'z':
|
|
case 'A': case 'B': case 'C': case 'D': case 'E':
|
|
case 'F': case 'G': case 'H': case 'I': case 'J':
|
|
case 'K': case 'L': case 'M': case 'N': case 'O':
|
|
case 'P': case 'Q': case 'R': case 'S': case 'T':
|
|
case 'U': case 'V': case 'W': case 'X': case 'Y':
|
|
case 'Z':
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
case '_':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline bool is_letter(char c)
|
|
{
|
|
switch (c)
|
|
{
|
|
case 'a': case 'b': case 'c': case 'd': case 'e':
|
|
case 'f': case 'g': case 'h': case 'i': case 'j':
|
|
case 'k': case 'l': case 'm': case 'n': case 'o':
|
|
case 'p': case 'q': case 'r': case 's': case 't':
|
|
case 'u': case 'v': case 'w': case 'x': case 'y':
|
|
case 'z':
|
|
case 'A': case 'B': case 'C': case 'D': case 'E':
|
|
case 'F': case 'G': case 'H': case 'I': case 'J':
|
|
case 'K': case 'L': case 'M': case 'N': case 'O':
|
|
case 'P': case 'Q': case 'R': case 'S': case 'T':
|
|
case 'U': case 'V': case 'W': case 'X': case 'Y':
|
|
case 'Z':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static inline bool is_number(char c)
|
|
{
|
|
return c >= '0' && c <= '9';
|
|
}
|
|
|
|
|
|
#define FNV1_PRIME 0x01000193u
|
|
#define FNV1_SEED 0x811C9DC5u
|
|
#define FNV1a(c, seed) ((uint32_t)((((unsigned)(c)) ^ (seed)) * FNV1_PRIME))
|
|
|
|
static inline uint32_t fnv1a(const char *key, uint32_t len)
|
|
{
|
|
uint32_t hash = FNV1_SEED;
|
|
for (uint32_t i = 0; i < len; i++)
|
|
{
|
|
hash = FNV1a(key[i], hash);
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
uint32_t size;
|
|
uint32_t capacity;
|
|
char data[];
|
|
} VHeader_;
|
|
|
|
static inline VHeader_* vec_new_(size_t element_size, size_t capacity)
|
|
{
|
|
assert(capacity < UINT32_MAX);
|
|
assert(element_size < UINT32_MAX / 100);
|
|
VHeader_ *header = CALLOC(element_size * capacity + sizeof(VHeader_));
|
|
header->capacity = (uint32_t)capacity;
|
|
return header;
|
|
}
|
|
|
|
static inline uint32_t vec_size(const void *vec)
|
|
{
|
|
if (!vec) return 0;
|
|
const VHeader_ *header = vec;
|
|
return header[-1].size;
|
|
}
|
|
|
|
static inline void vec_resize(void *vec, uint32_t new_size)
|
|
{
|
|
if (!vec) return;
|
|
VHeader_ *header = vec;
|
|
header[-1].size = new_size;
|
|
}
|
|
static inline void vec_pop(void *vec)
|
|
{
|
|
assert(vec);
|
|
assert(vec_size(vec) > 0);
|
|
VHeader_ *header = vec;
|
|
header[-1].size--;
|
|
}
|
|
static inline void* expand_(void *vec, size_t element_size)
|
|
{
|
|
VHeader_ *header;
|
|
if (!vec)
|
|
{
|
|
header = vec_new_(element_size, 8);
|
|
}
|
|
else
|
|
{
|
|
header = ((VHeader_ *)vec) - 1;
|
|
}
|
|
if (header->size == header->capacity)
|
|
{
|
|
VHeader_ *new_array = vec_new_(element_size, header->capacity << 1U);
|
|
#if IS_GCC
|
|
// I've yet to figure out why GCC insists that this is trying to copy
|
|
// 8 bytes over a size zero array.
|
|
// We use volatile to deoptimize on GCC
|
|
volatile size_t copy_size = element_size * header->capacity + sizeof(VHeader_);
|
|
#else
|
|
size_t copy_size = element_size * header->capacity + sizeof(VHeader_);
|
|
#endif
|
|
memcpy(new_array, header, copy_size);
|
|
header = new_array;
|
|
new_array->capacity = header->capacity << 1U;
|
|
}
|
|
header->size++;
|
|
return &(header[1]);
|
|
}
|
|
|
|
|
|
#define CONCAT_INNER(a, b) a ## b
|
|
#define CONCAT(a, b) CONCAT_INNER(a, b)
|
|
#define VECEACH(_vec, _index) \
|
|
for (unsigned (_index) = 0, CONCAT(__vecsize_, __LINE__) = vec_size(_vec); (_index) < CONCAT(__vecsize_, __LINE__); (_index)++)
|
|
#define foreach(_vec, _index) \
|
|
for (unsigned (_index) = 0, CONCAT(__vecsize_, __LINE__) = vec_size(_vec); (_index) < CONCAT(__vecsize_, __LINE__); (_index)++)
|
|
|
|
|
|
#define VECNEW(_type, _capacity) ((_type *)(vec_new_(sizeof(_type), _capacity) + 1))
|
|
#define vec_add(vec_, value_) do { \
|
|
void *__temp = expand_((vec_), sizeof(*(vec_))); \
|
|
(vec_) = __temp; \
|
|
(vec_)[vec_size(vec_) - 1] = value_; \
|
|
} while (0)
|
|
|
|
#define vec_insert_first(vec_, value_) do { \
|
|
void *__temp = expand_((vec_), sizeof(*(vec_))); \
|
|
(vec_) = __temp; \
|
|
unsigned __xsize = vec_size(vec_); \
|
|
for (unsigned __x = __xsize - 1; __x > 0; __x--) (vec_)[__x] = (vec_)[__x - 1]; \
|
|
(vec_)[0] = value_; \
|
|
} while (0)
|
|
|
|
#if IS_GCC || IS_CLANG
|
|
#define VECLAST(_vec) ({ unsigned _size = vec_size(_vec); _size ? (_vec)[_size - 1] : NULL; })
|
|
#else
|
|
#define VECLAST(_vec) (vec_size(_vec) ? (_vec)[vec_size(_vec) - 1] : NULL)
|
|
#endif
|
|
|
|
|
|
static inline bool is_all_upper(const char* string)
|
|
{
|
|
char c;
|
|
while ((c = *(string++)) != '\0')
|
|
{
|
|
if (is_lower(c)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static inline bool is_all_lower(const char* string)
|
|
{
|
|
char c;
|
|
while ((c = *(string++)) != '\0')
|
|
{
|
|
if (is_upper(c)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#ifndef __printflike
|
|
#define __printflike(x, y)
|
|
#endif
|
|
|
|
typedef struct StringSlice_
|
|
{
|
|
const char *ptr;
|
|
size_t len;
|
|
} StringSlice;
|
|
|
|
char *strcat_arena(const char *a, const char *b);
|
|
char *strformat(const char *var, ...) __printflike(1, 2);
|
|
char *strcopy(const char *start, size_t len);
|
|
StringSlice strnexttok(StringSlice *slice, char separator);
|
|
static inline bool slicestrcmp(StringSlice slice, const char *other)
|
|
{
|
|
if (strlen(other) != slice.len) return false;
|
|
return strncmp(slice.ptr, other, slice.len) == 0;
|
|
}
|
|
|
|
static inline StringSlice strtoslice(const char *data)
|
|
{
|
|
return (StringSlice) { data, strlen(data) };
|
|
}
|
|
void slicetrim(StringSlice *slice);
|
|
|
|
#if IS_GCC || IS_CLANG
|
|
|
|
#define MAX(_a, _b) ({ \
|
|
typeof(_a) __a__ = (_a); \
|
|
typeof(_b) __b__ = (_b); \
|
|
__a__ > __b__ ? __a__ : __b__; })
|
|
|
|
#define MIN(_a, _b) ({ \
|
|
typeof(_a) __a__ = (_a); \
|
|
typeof(_b) __b__ = (_b); \
|
|
__a__ < __b__ ? __a__ : __b__; })
|
|
|
|
#else
|
|
#define MAX(a_, b_) ((a_) > (b_) ? (a_) : (b_))
|
|
#define MIN(a_, b_) ((a_) < (b_) ? (a_) : (b_))
|
|
#endif
|
|
// Windows-specific code
|
|
|
|
|
|
#if PLATFORM_WINDOWS
|
|
|
|
int asprintf(char **strp, const char *fmt, ...);
|
|
int vasprintf(char **strp, const char *fmt, va_list ap);
|
|
char *strndup(const char *s, size_t len);
|
|
|
|
char *realpath(const char *path, char *resolved_path);
|
|
|
|
#define mkdir(name, unused) _mkdir(name)
|
|
|
|
#endif
|