Delete old version files
This commit is contained in:
365
common.c
365
common.c
@@ -1,365 +0,0 @@
|
|||||||
|
|
||||||
function S64
|
|
||||||
clamp_top_s64(S64 val, S64 max){
|
|
||||||
if(val>max)return max;
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SizeU
|
|
||||||
max_sizeu(SizeU a, SizeU b){
|
|
||||||
if(a>b) return a;
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
function U64
|
|
||||||
hash_fnv(String string) {
|
|
||||||
U64 hash = (U64)14695981039346656037ULL;
|
|
||||||
for (U64 i = 0; i < string.len; i++) {
|
|
||||||
hash = hash ^ (U64)(string.str[i]);
|
|
||||||
hash = hash * (U64)1099511628211ULL;
|
|
||||||
}
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
function U64
|
|
||||||
hash_u64(U64 x) {
|
|
||||||
x *= 0xff51afd7ed558ccd;
|
|
||||||
x ^= x >> 32;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
function U64
|
|
||||||
hash_ptr(const void *ptr) {
|
|
||||||
return hash_u64((uintptr_t)ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
function U64
|
|
||||||
hash_mix(U64 x, U64 y) {
|
|
||||||
x ^= y;
|
|
||||||
x *= 0xff51afd7ed558ccd;
|
|
||||||
x ^= x >> 32;
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
function U64
|
|
||||||
is_pow2(U64 x) {
|
|
||||||
assert(x != 0);
|
|
||||||
B32 result = (x & (x - 1llu)) == 0;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function U64
|
|
||||||
wrap_around_pow2(U64 x, U64 power_of_2) {
|
|
||||||
assert(is_pow2(power_of_2));
|
|
||||||
U64 r = (((x)&((power_of_2)-1llu)));
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Strings
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
global String string_empty;
|
|
||||||
|
|
||||||
function B32
|
|
||||||
string_compare(String a, String b){
|
|
||||||
if(a.len != b.len)
|
|
||||||
return false;
|
|
||||||
for(S64 i = 0; i < a.len; i++){
|
|
||||||
if(a.str[i] != b.str[i])
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function U8
|
|
||||||
char_to_lower(U8 c){
|
|
||||||
if(c >= 'A' && c <= 'Z')
|
|
||||||
c += 32;
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
function U8
|
|
||||||
char_to_upper(U8 c){
|
|
||||||
if(c >= 'a' && c <= 'z')
|
|
||||||
c -= 32;
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
|
|
||||||
function B32
|
|
||||||
string_is_empty(String a){
|
|
||||||
B32 result = a.len == 0;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function String
|
|
||||||
string_to_lower(Arena *arena, String string){
|
|
||||||
String result = arena_push_string_copy(arena, string);
|
|
||||||
for(S64 i = 0; i < string.len; i++){
|
|
||||||
result.str[i] = char_to_lower(result.str[i]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Per Vognsen's like Map to pointers
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
typedef struct Map_Key_Val{
|
|
||||||
U64 key;
|
|
||||||
void *value;
|
|
||||||
}Map_Key_Val;
|
|
||||||
|
|
||||||
typedef struct Map{
|
|
||||||
Map_Key_Val *data;
|
|
||||||
int len;
|
|
||||||
int cap;
|
|
||||||
}Map;
|
|
||||||
|
|
||||||
function void map_insert_u64(Map *map, U64 key, void *val);
|
|
||||||
function int max_int(int a, int b);
|
|
||||||
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
function void
|
|
||||||
map_grow(Map *map, int new_size){
|
|
||||||
new_size = max_int(16, new_size);
|
|
||||||
assert(new_size > map->cap);
|
|
||||||
assert(is_pow2(new_size));
|
|
||||||
Map new_map = {
|
|
||||||
.data = calloc(new_size, sizeof(Map_Key_Val)),
|
|
||||||
.cap = new_size,
|
|
||||||
};
|
|
||||||
for(int i = 0; i < map->cap; i++){
|
|
||||||
if(map->data[i].key){
|
|
||||||
map_insert_u64(&new_map, map->data[i].key, map->data[i].value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(map->data) free(map->data);
|
|
||||||
*map = new_map;
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
map_insert_u64(Map *map, U64 key, void *val){
|
|
||||||
assert(val);
|
|
||||||
if(key == 0) key++;
|
|
||||||
if((2*map->len) + 1 > map->cap){
|
|
||||||
map_grow(map, 2*map->cap);
|
|
||||||
}
|
|
||||||
U64 hash = hash_u64(key);
|
|
||||||
U64 index = wrap_around_pow2(hash, map->cap);
|
|
||||||
U64 i = index;
|
|
||||||
for(;;){
|
|
||||||
if(map->data[i].key == 0){
|
|
||||||
map->len++;
|
|
||||||
map->data[i].key = key;
|
|
||||||
map->data[i].value = val;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(map->data[i].key == key){
|
|
||||||
map->data[i].value = val;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
i = wrap_around_pow2(i+1, map->cap);
|
|
||||||
if(i == map->cap){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function void *
|
|
||||||
map_get_u64(Map *map, U64 key){
|
|
||||||
if(map->len == 0) return 0;
|
|
||||||
if(key == 0) key++;
|
|
||||||
U64 hash = hash_u64(key);
|
|
||||||
U64 index = wrap_around_pow2(hash, map->cap);
|
|
||||||
U64 i = index;
|
|
||||||
for(;;){
|
|
||||||
if(map->data[i].key == key){
|
|
||||||
return map->data[i].value;
|
|
||||||
}
|
|
||||||
else if(map->data[i].key == 0){
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
i = wrap_around_pow2(i+1, map->cap);
|
|
||||||
if(i == map->cap){
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function void *
|
|
||||||
map_get(Map *map, void *pointer){
|
|
||||||
return map_get_u64(map, (U64)pointer);
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
map_insert(Map *map, void *key, void *value){
|
|
||||||
map_insert_u64(map, (U64)key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
map_test(){
|
|
||||||
Map map = {0};
|
|
||||||
const SizeU size = 1025;
|
|
||||||
for(SizeU i = 1; i < size; i++){
|
|
||||||
map_insert_u64(&map, i, (void *)i);
|
|
||||||
}
|
|
||||||
for(SizeU i = 1; i < size; i++){
|
|
||||||
SizeU val = (SizeU)map_get_u64(&map, i);
|
|
||||||
assert(val == i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Array
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
typedef struct Array_Head{
|
|
||||||
int cap, len;
|
|
||||||
}Array_Head;
|
|
||||||
|
|
||||||
#define array_get_head(x) (((Array_Head *)(x)) - 1)
|
|
||||||
#define array_cap(x) array_get_head(x)->cap
|
|
||||||
#define array_len(x) array_get_head(x)->len
|
|
||||||
#define array_push(arr,i) (array_grow((void **)&arr, sizeof(arr[0])), (arr)[array_len(arr)++] = (i))
|
|
||||||
#define array_init(arr,cap) array__init((void **)&arr,sizeof(arr[0]), cap)
|
|
||||||
|
|
||||||
|
|
||||||
function void
|
|
||||||
array__init(void **array, SizeU sizeof_item, int cap){
|
|
||||||
Array_Head *head = malloc(sizeof_item*cap + sizeof(Array_Head));
|
|
||||||
head->cap = cap;
|
|
||||||
head->len = 0;
|
|
||||||
*array = head + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
array_grow(void **array, SizeU sizeof_item){
|
|
||||||
if(*array == 0){
|
|
||||||
array__init(array, sizeof_item, 16);
|
|
||||||
}
|
|
||||||
else if(array_len(*array) + 1 > array_cap(*array)){
|
|
||||||
Array_Head *head = array_get_head(*array);
|
|
||||||
SizeU len = head->len;
|
|
||||||
SizeU cap = head->cap * 2;
|
|
||||||
head = realloc(head, sizeof_item*cap + sizeof(Array_Head));
|
|
||||||
head->cap = cap;
|
|
||||||
head->len = len;
|
|
||||||
*array = head + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
array_test(){
|
|
||||||
int *array = 0;
|
|
||||||
for(int i = 0; i < 100; i++){
|
|
||||||
array_push(array, i);
|
|
||||||
}
|
|
||||||
for(int i = 0; i < 100; i++){
|
|
||||||
assert(array[i] == i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// String interning
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
typedef struct Table_Index{
|
|
||||||
U64 hash;
|
|
||||||
U64 index;
|
|
||||||
U64 iter;
|
|
||||||
U64 max_size;
|
|
||||||
}Table_Index;
|
|
||||||
|
|
||||||
typedef struct Intern_String{
|
|
||||||
String s;
|
|
||||||
}Intern_String;
|
|
||||||
|
|
||||||
typedef struct Intern_Table{
|
|
||||||
S64 interns_in_bytes;
|
|
||||||
S64 interns_inserted;
|
|
||||||
S64 interns_max;
|
|
||||||
Intern_String *interns;
|
|
||||||
Arena *arena;
|
|
||||||
}Intern_Table;
|
|
||||||
|
|
||||||
function Intern_Table
|
|
||||||
intern_table(Arena *arena, SizeU size){
|
|
||||||
Intern_Table result = {0};
|
|
||||||
result.arena = arena;
|
|
||||||
result.interns = arena_push_array(arena, Intern_String, size);
|
|
||||||
result.interns_max = size;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Table_Index
|
|
||||||
table_index_from_hash(U64 hash, U64 max_size){
|
|
||||||
Table_Index result = {0};
|
|
||||||
result.hash = hash;
|
|
||||||
result.index = result.hash % max_size;
|
|
||||||
result.iter = result.index;
|
|
||||||
result.max_size = max_size;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Table_Index
|
|
||||||
table_index_from_string(String string, U64 max_size){
|
|
||||||
U64 hash = hash_fnv(string);
|
|
||||||
Table_Index result = table_index_from_hash(hash, max_size);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function B32
|
|
||||||
table_index_advance(Table_Index *index){
|
|
||||||
index->iter = wrap_around_pow2(index->iter + 1, index->max_size);
|
|
||||||
B32 result = index->iter == index->index;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Intern_String
|
|
||||||
intern_string(Intern_Table *p, String string){
|
|
||||||
Intern_String result = {0};
|
|
||||||
Table_Index index = table_index_from_string(string, p->interns_max);
|
|
||||||
for(;;){
|
|
||||||
Intern_String *intern = p->interns + index.iter;
|
|
||||||
if(intern->s.str == 0){
|
|
||||||
result.s = arena_push_string_copy(p->arena, string);
|
|
||||||
p->interns_in_bytes += string.len;
|
|
||||||
p->interns_inserted += 1;
|
|
||||||
*intern = result;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if(string_compare(intern->s, string)){
|
|
||||||
result = *intern;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (table_index_advance(&index))
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function B32
|
|
||||||
intern_compare(Intern_String a, Intern_String b){
|
|
||||||
B32 result = a.s.str == b.s.str;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
intern_test(){
|
|
||||||
Arena *scratch = arena_begin_scratch();
|
|
||||||
Intern_Table table = intern_table(scratch, 512);
|
|
||||||
assert(intern_compare(intern_string(&table, lit("Thing")),
|
|
||||||
intern_string(&table, lit("Thing"))));
|
|
||||||
assert(!intern_compare(intern_string(&table, lit("Thing")),
|
|
||||||
intern_string(&table, lit("No_Thing"))));
|
|
||||||
assert(intern_compare(intern_string(&table, lit("No_Thing")),
|
|
||||||
intern_string(&table, lit("No_Thing"))));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
86
lang.h
86
lang.h
@@ -1,86 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#define global static
|
|
||||||
#define function static
|
|
||||||
|
|
||||||
#define assert(x) do{if(!(x)) __debugbreak();}while(0)
|
|
||||||
#define assert_msg(x,...) assert(x)
|
|
||||||
#define not_implemented assert_msg(0, "Not implemented")
|
|
||||||
#define invalid_codepath assert_msg(0, "Invalid codepath")
|
|
||||||
|
|
||||||
#define buff_cap(x) (sizeof(x)/sizeof((x)[0]))
|
|
||||||
#define lit(x) ((String){(U8*)x,buff_cap(x)-1})
|
|
||||||
#define meta(x)
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
typedef int8_t S8;
|
|
||||||
typedef int16_t S16;
|
|
||||||
typedef int32_t S32;
|
|
||||||
typedef int64_t S64;
|
|
||||||
typedef uint8_t U8;
|
|
||||||
typedef uint16_t U16;
|
|
||||||
typedef uint32_t U32;
|
|
||||||
typedef uint64_t U64;
|
|
||||||
typedef S8 B8;
|
|
||||||
typedef S16 B16;
|
|
||||||
typedef S32 B32;
|
|
||||||
typedef S64 B64;
|
|
||||||
typedef uint64_t SizeU;
|
|
||||||
typedef int64_t SizeI;
|
|
||||||
typedef float F32;
|
|
||||||
typedef double F64;
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
//const B32 true = 1;
|
|
||||||
//const B32 false = 0;
|
|
||||||
#define kib(x) ((x)*1024llu)
|
|
||||||
#define mib(x) (kib(x)*1024llu)
|
|
||||||
#define gib(x) (mib(x)*1024llu)
|
|
||||||
#define string_expand(x) (int)x.len, x.str
|
|
||||||
|
|
||||||
typedef struct String_Node String_Node;
|
|
||||||
typedef struct String_List String_List;
|
|
||||||
typedef struct String{
|
|
||||||
U8 *str;
|
|
||||||
S64 len;
|
|
||||||
}String;
|
|
||||||
|
|
||||||
struct String_Node{
|
|
||||||
String_Node *next;
|
|
||||||
union{
|
|
||||||
String string;
|
|
||||||
struct{U8*str; S64 len;};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct String_List{
|
|
||||||
String_Node *first;
|
|
||||||
String_Node *last;
|
|
||||||
S64 char_count;
|
|
||||||
S64 node_count;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SLLQueuePushMod(f,l,n,next) do{\
|
|
||||||
if((f)==0){\
|
|
||||||
(f)=(l)=(n);\
|
|
||||||
}\
|
|
||||||
else{\
|
|
||||||
(l)=(l)->next=(n);\
|
|
||||||
} \
|
|
||||||
}while(0)
|
|
||||||
|
|
||||||
#define SLLQueuePush(f,l,n) SLLQueuePushMod(f,l,n,next)
|
|
||||||
|
|
||||||
|
|
||||||
#define SLLStackPush(l,n) do{\
|
|
||||||
(n)->next = (l);\
|
|
||||||
(l) = (n);\
|
|
||||||
}while(0)
|
|
||||||
|
|
||||||
#define SLLStackPop(l,n) do{\
|
|
||||||
if(l){\
|
|
||||||
(n) = (l);\
|
|
||||||
(l) = (l)->next;\
|
|
||||||
(n)->next = 0;\
|
|
||||||
}\
|
|
||||||
}while(0)
|
|
||||||
642
lex.c
642
lex.c
@@ -1,642 +0,0 @@
|
|||||||
global Intern_String keyword_if;
|
|
||||||
global Intern_String keyword_for;
|
|
||||||
global Intern_String keyword_cast;
|
|
||||||
global Intern_String keyword_else;
|
|
||||||
global Intern_String keyword_defer;
|
|
||||||
global Intern_String keyword_do;
|
|
||||||
global Intern_String keyword_size_type;
|
|
||||||
global Intern_String keyword_size_expr;
|
|
||||||
global Intern_String keyword_const;
|
|
||||||
global Intern_String keyword_typedef;
|
|
||||||
global Intern_String keyword_return;
|
|
||||||
global Intern_String keyword_typeof;
|
|
||||||
global Intern_String keyword_while;
|
|
||||||
global Intern_String keyword_switch;
|
|
||||||
global Intern_String keyword_case;
|
|
||||||
global Intern_String keyword_struct;
|
|
||||||
global Intern_String keyword_enum;
|
|
||||||
global Intern_String keyword_union;
|
|
||||||
global U8 *first_keyword;
|
|
||||||
global U8 *last_keyword;
|
|
||||||
|
|
||||||
global Intern_String intern_char;
|
|
||||||
global Intern_String intern_void;
|
|
||||||
global Intern_String intern_int;
|
|
||||||
|
|
||||||
function void
|
|
||||||
init_default_keywords(Intern_Table *t){
|
|
||||||
keyword_if = intern_string(t, lit("if"));
|
|
||||||
first_keyword = keyword_if.s.str;
|
|
||||||
|
|
||||||
keyword_cast = intern_string(t, lit("cast"));
|
|
||||||
keyword_for = intern_string(t, lit("for"));
|
|
||||||
keyword_else = intern_string(t, lit("else"));
|
|
||||||
keyword_defer = intern_string(t, lit("defer"));
|
|
||||||
keyword_do = intern_string(t, lit("do"));
|
|
||||||
keyword_size_type = intern_string(t, lit("size_type"));
|
|
||||||
keyword_size_expr = intern_string(t, lit("size_expr"));
|
|
||||||
keyword_typeof = intern_string(t, lit("typeof"));
|
|
||||||
keyword_const = intern_string(t, lit("const"));
|
|
||||||
keyword_while = intern_string(t, lit("while"));
|
|
||||||
keyword_return = intern_string(t, lit("return"));
|
|
||||||
keyword_switch = intern_string(t, lit("switch"));
|
|
||||||
keyword_typedef = intern_string(t, lit("typedef"));
|
|
||||||
keyword_case = intern_string(t, lit("case"));
|
|
||||||
keyword_struct = intern_string(t, lit("struct"));
|
|
||||||
keyword_enum = intern_string(t, lit("enum"));
|
|
||||||
|
|
||||||
keyword_union = intern_string(t, lit("union"));
|
|
||||||
last_keyword = keyword_union.s.str;
|
|
||||||
|
|
||||||
intern_char = intern_string(t, lit("char"));
|
|
||||||
intern_void = intern_string(t, lit("void"));
|
|
||||||
intern_int = intern_string(t, lit("int"));
|
|
||||||
}
|
|
||||||
|
|
||||||
function B32
|
|
||||||
lex_is_keyword(Intern_String str){
|
|
||||||
B32 result = str.s.str >= first_keyword && str.s.str <= last_keyword;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef enum Token_Kind{
|
|
||||||
TK_End,
|
|
||||||
|
|
||||||
TK_Mul,
|
|
||||||
TK_Div,
|
|
||||||
TK_Mod,
|
|
||||||
TK_LeftShift,
|
|
||||||
TK_RightShift,
|
|
||||||
TK_FirstMul = TK_Mul,
|
|
||||||
TK_LastMul = TK_RightShift,
|
|
||||||
|
|
||||||
TK_Add,
|
|
||||||
TK_Sub,
|
|
||||||
TK_FirstAdd = TK_Add,
|
|
||||||
TK_LastAdd = TK_Sub,
|
|
||||||
|
|
||||||
TK_Equals,
|
|
||||||
TK_LesserThenOrEqual,
|
|
||||||
TK_GreaterThenOrEqual,
|
|
||||||
TK_LesserThen,
|
|
||||||
TK_GreaterThen,
|
|
||||||
TK_NotEquals,
|
|
||||||
TK_FirstCompare = TK_Equals,
|
|
||||||
TK_LastCompare = TK_NotEquals,
|
|
||||||
|
|
||||||
TK_BitAnd,
|
|
||||||
TK_BitOr,
|
|
||||||
TK_Pointer,
|
|
||||||
TK_And,
|
|
||||||
TK_Or,
|
|
||||||
TK_FirstLogical = TK_BitAnd,
|
|
||||||
TK_LastLogical = TK_Or,
|
|
||||||
|
|
||||||
TK_Neg,
|
|
||||||
TK_Not,
|
|
||||||
TK_OpenParen,
|
|
||||||
TK_CloseParen,
|
|
||||||
TK_OpenBrace,
|
|
||||||
TK_CloseBrace,
|
|
||||||
TK_OpenBracket,
|
|
||||||
TK_CloseBracket,
|
|
||||||
TK_Comma,
|
|
||||||
TK_Pound,
|
|
||||||
TK_Question,
|
|
||||||
TK_ThreeDots,
|
|
||||||
TK_Semicolon,
|
|
||||||
TK_Dot,
|
|
||||||
|
|
||||||
TK_Colon,
|
|
||||||
|
|
||||||
TK_Assign,
|
|
||||||
TK_ColonAssign,
|
|
||||||
TK_DivAssign,
|
|
||||||
TK_MulAssign,
|
|
||||||
TK_ModAssign,
|
|
||||||
TK_SubAssign,
|
|
||||||
TK_AddAssign,
|
|
||||||
TK_AndAssign,
|
|
||||||
TK_OrAssign,
|
|
||||||
TK_XorAssign,
|
|
||||||
TK_LeftShiftAssign,
|
|
||||||
TK_RightShiftAssign,
|
|
||||||
TK_FirstAssign = TK_Assign,
|
|
||||||
TK_LastAssign = TK_RightShiftAssign,
|
|
||||||
|
|
||||||
TK_DoubleColon,
|
|
||||||
TK_At,
|
|
||||||
TK_Decrement,
|
|
||||||
TK_Increment,
|
|
||||||
TK_PostDecrement,
|
|
||||||
TK_PostIncrement,
|
|
||||||
|
|
||||||
TK_Arrow,
|
|
||||||
TK_ExprSizeof,
|
|
||||||
TK_DocComment,
|
|
||||||
TK_Comment,
|
|
||||||
TK_Identifier,
|
|
||||||
TK_StringLit,
|
|
||||||
TK_Character,
|
|
||||||
TK_Error,
|
|
||||||
TK_Float,
|
|
||||||
TK_Integer,
|
|
||||||
TK_Keyword,
|
|
||||||
}Token_Kind;
|
|
||||||
|
|
||||||
typedef struct Token{
|
|
||||||
Token_Kind kind;
|
|
||||||
union{
|
|
||||||
String string;
|
|
||||||
struct{U8 *str; S64 len;};
|
|
||||||
};
|
|
||||||
|
|
||||||
union {
|
|
||||||
U64 int_val;
|
|
||||||
F64 float_val;
|
|
||||||
String error_val;
|
|
||||||
Intern_String intern_val;
|
|
||||||
};
|
|
||||||
|
|
||||||
String file;
|
|
||||||
S32 line;
|
|
||||||
U8 *line_begin;
|
|
||||||
}Token;
|
|
||||||
#include "token_array.c"
|
|
||||||
|
|
||||||
typedef struct Lex_Stream{
|
|
||||||
String stream;
|
|
||||||
S64 iter;
|
|
||||||
|
|
||||||
U8 *line_begin;
|
|
||||||
String file;
|
|
||||||
S32 line;
|
|
||||||
}Lex_Stream;
|
|
||||||
|
|
||||||
|
|
||||||
function U8
|
|
||||||
lexc(Lex_Stream *s){
|
|
||||||
return s->stream.str[s->iter];
|
|
||||||
}
|
|
||||||
|
|
||||||
function U8
|
|
||||||
lexci(Lex_Stream *s, S32 i){
|
|
||||||
return s->stream.str[s->iter+i];
|
|
||||||
}
|
|
||||||
|
|
||||||
function U8 *
|
|
||||||
lexcp(Lex_Stream *s){
|
|
||||||
return s->stream.str + s->iter;
|
|
||||||
}
|
|
||||||
|
|
||||||
function B32
|
|
||||||
lex_is_whitespace(U8 c){
|
|
||||||
B32 result = c == '\n' || c == '\r' || c == ' ' || c == '\r';
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function B32
|
|
||||||
lex_is_alphabetic(U8 c){
|
|
||||||
B32 result = (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function B32
|
|
||||||
lex_is_numeric(U8 c){
|
|
||||||
B32 result = c >= '0' && c <= '9';
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function B32
|
|
||||||
lex_is_alphanumeric(U8 c){
|
|
||||||
B32 result = lex_is_numeric(c) || lex_is_alphabetic(c);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
lex_set_len(Lex_Stream *s, Token *token){
|
|
||||||
assert(lexcp(s) >= token->str);
|
|
||||||
token->len = lexcp(s) - token->str;
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
token_error(Token *t, String error_val){
|
|
||||||
t->kind = TK_Error;
|
|
||||||
t->error_val = error_val;
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
lex_parse_u64(Token *t){
|
|
||||||
U64 result = 0;
|
|
||||||
U64 m = 1;
|
|
||||||
for(S64 i = t->len - 1; i >= 0; --i){
|
|
||||||
U64 val = t->str[i] - '0';
|
|
||||||
U64 new_val = val * m;
|
|
||||||
if((result + new_val) < result){
|
|
||||||
token_error(t, lit("Integer overflow"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
result+=new_val;
|
|
||||||
m *= 10;
|
|
||||||
}
|
|
||||||
t->int_val = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
lex_advance(Lex_Stream *s){
|
|
||||||
if(s->iter >= s->stream.len){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if(lexc(s) == '\n'){
|
|
||||||
s->iter++;
|
|
||||||
s->line++;
|
|
||||||
s->line_begin = lexcp(s);
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
s->iter++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
lex_parse_string(Lex_Stream *s, Token *t, U8 c){
|
|
||||||
for(;;){
|
|
||||||
if(lexc(s) == '\\') lex_advance(s);
|
|
||||||
else if(lexc(s) == c) break;
|
|
||||||
else if(lexc(s) == 0){
|
|
||||||
token_error(t, lit("Unterminated string, reached end of file"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
lex_advance(s);
|
|
||||||
}
|
|
||||||
if(t->kind != TK_Error){
|
|
||||||
lex_advance(s);
|
|
||||||
lex_set_len(s,t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#define CASE2(op, OpName, Assign) \
|
|
||||||
case op: \
|
|
||||||
if (lexc(s) == '=') { \
|
|
||||||
lex_advance(s); \
|
|
||||||
t.kind = Assign; \
|
|
||||||
} else { \
|
|
||||||
t.kind = OpName; \
|
|
||||||
} \
|
|
||||||
break
|
|
||||||
#define CASE3(op, OpName, Assign, Incr) \
|
|
||||||
case op: \
|
|
||||||
if (lexc(s) == '=') { \
|
|
||||||
lex_advance(s); \
|
|
||||||
t.kind = Assign; \
|
|
||||||
} else if (lexc(s) == op) { \
|
|
||||||
lex_advance(s); \
|
|
||||||
t.kind = Incr; \
|
|
||||||
} else { \
|
|
||||||
t.kind = OpName; \
|
|
||||||
} \
|
|
||||||
break
|
|
||||||
|
|
||||||
function void
|
|
||||||
lex__stream(Token_Array *array, Lex_Stream *s){
|
|
||||||
while(lexc(s)){
|
|
||||||
while(lex_is_whitespace(lexc(s)))
|
|
||||||
lex_advance(s);
|
|
||||||
|
|
||||||
Token t = {0};
|
|
||||||
t.str = lexcp(s);
|
|
||||||
t.file = s->file;
|
|
||||||
t.line = s->line;
|
|
||||||
t.line_begin = s->line_begin;
|
|
||||||
lex_advance(s);
|
|
||||||
|
|
||||||
switch(*t.str){
|
|
||||||
case 0: break;
|
|
||||||
case '@': t.kind = TK_At; break;
|
|
||||||
case '(': t.kind = TK_OpenParen; break;
|
|
||||||
case ')': t.kind = TK_CloseParen; break;
|
|
||||||
case '{': t.kind = TK_OpenBrace; break;
|
|
||||||
case '}': t.kind = TK_CloseBrace; break;
|
|
||||||
case '[': t.kind = TK_OpenBracket; break;
|
|
||||||
case ']': t.kind = TK_CloseBracket; break;
|
|
||||||
case ',': t.kind = TK_Comma; break;
|
|
||||||
case '~': t.kind = TK_Neg; break;
|
|
||||||
case '?': t.kind = TK_Question; break;
|
|
||||||
case ';': t.kind = TK_Semicolon; break;
|
|
||||||
case '#': t.kind = TK_Pound; break;
|
|
||||||
CASE2('!', TK_Not, TK_NotEquals);
|
|
||||||
CASE2('^', TK_Pointer, TK_XorAssign);
|
|
||||||
CASE2('=', TK_Assign, TK_Equals);
|
|
||||||
CASE2('*', TK_Mul, TK_MulAssign);
|
|
||||||
CASE2('%', TK_Mod, TK_ModAssign);
|
|
||||||
CASE3('+', TK_Add, TK_AddAssign, TK_Increment);
|
|
||||||
CASE3('&', TK_BitAnd, TK_AndAssign, TK_And);
|
|
||||||
CASE3('|', TK_BitOr, TK_OrAssign, TK_Or);
|
|
||||||
#undef CASE2
|
|
||||||
#undef CASE3
|
|
||||||
case '.': {
|
|
||||||
if(lexc(s) == '.' && lexci(s,1) == '.') {
|
|
||||||
lex_advance(s); lex_advance(s);
|
|
||||||
t.kind = TK_ThreeDots;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
t.kind = TK_Dot;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
|
|
||||||
case '<': {
|
|
||||||
if (lexc(s) == '<') {
|
|
||||||
lex_advance(s);
|
|
||||||
if (lexc(s) == '=') {
|
|
||||||
lex_advance(s);
|
|
||||||
t.kind = TK_LeftShiftAssign;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
t.kind = TK_LeftShift;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (lexc(s) == '=') {
|
|
||||||
lex_advance(s);
|
|
||||||
t.kind = TK_LesserThenOrEqual;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
t.kind = TK_LesserThen;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case '>': {
|
|
||||||
if (lexc(s) == '>') {
|
|
||||||
lex_advance(s);
|
|
||||||
if (lexc(s) == '=') {
|
|
||||||
lex_advance(s);
|
|
||||||
t.kind = TK_RightShiftAssign;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
t.kind = TK_RightShift;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (lexc(s) == '=') {
|
|
||||||
lex_advance(s);
|
|
||||||
t.kind = TK_GreaterThenOrEqual;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
t.kind = TK_GreaterThen;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case ':': {
|
|
||||||
if (lexc(s) == ':') {
|
|
||||||
lex_advance(s);
|
|
||||||
t.kind = TK_DoubleColon;
|
|
||||||
}
|
|
||||||
else if(lexc(s) == '='){
|
|
||||||
lex_advance(s);
|
|
||||||
t.kind = TK_ColonAssign;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
t.kind = TK_Colon;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case '-':{
|
|
||||||
if (lexc(s) == '=') {
|
|
||||||
lex_advance(s);
|
|
||||||
t.kind = TK_SubAssign;
|
|
||||||
}
|
|
||||||
else if (lexc(s) == '-') {
|
|
||||||
lex_advance(s);
|
|
||||||
t.kind = TK_Decrement;
|
|
||||||
}
|
|
||||||
else if (lexc(s) == '>') {
|
|
||||||
lex_advance(s);
|
|
||||||
t.kind = TK_Arrow;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
t.kind = TK_Sub;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
|
|
||||||
case '\'':{not_implemented;} break;
|
|
||||||
case '"': {
|
|
||||||
t.kind = TK_StringLit;
|
|
||||||
lex_parse_string(s,&t,'"');
|
|
||||||
if(t.kind != TK_Error){
|
|
||||||
t.str += 1;
|
|
||||||
t.len -= 2;
|
|
||||||
}
|
|
||||||
t.intern_val = intern_string(&array->interns, t.string);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case '/': {
|
|
||||||
if(lexc(s) == '='){
|
|
||||||
t.kind = TK_DivAssign;
|
|
||||||
lex_advance(s);
|
|
||||||
}
|
|
||||||
else if(lexc(s) == '/'){
|
|
||||||
lex_advance(s);
|
|
||||||
t.kind = TK_Comment;
|
|
||||||
for(;;){
|
|
||||||
if(lexc(s) == '\n' || lexc(s) == 0) break;
|
|
||||||
lex_advance(s);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if(lexc(s) == '*'){
|
|
||||||
lex_advance(s);
|
|
||||||
t.kind = TK_Comment;
|
|
||||||
for(;;){
|
|
||||||
if(lexc(s) == '*' && lexci(s,1) == '/'){
|
|
||||||
lex_advance(s);
|
|
||||||
lex_advance(s);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if(lexc(s) == 0){
|
|
||||||
token_error(&t, lit("Unterminated block comment"));
|
|
||||||
goto skip_continue;
|
|
||||||
}
|
|
||||||
lex_advance(s);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
skip_continue:;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
t.kind = TK_Div;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case '0':case '1':case '2':case '3':case '4':
|
|
||||||
case '5':case '6':case '7':case '8':case '9':{
|
|
||||||
t.kind = TK_Integer;
|
|
||||||
while(lex_is_numeric(lexc(s)))
|
|
||||||
lex_advance(s);
|
|
||||||
lex_set_len(s, &t);
|
|
||||||
lex_parse_u64(&t);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case 'A':case 'a':case 'M':case 'm':case 'B':
|
|
||||||
case 'b':case 'N':case 'n':case 'C':case 'c':case 'O':
|
|
||||||
case 'o':case 'D':case 'd':case 'P':case 'p':case 'E':
|
|
||||||
case 'e':case 'Q':case 'q':case 'F':case 'f':case 'R':
|
|
||||||
case 'r':case 'G':case 'g':case 'S':case 's':case 'H':
|
|
||||||
case 'h':case 'T':case 't':case 'I':case 'i':case 'U':
|
|
||||||
case 'u':case 'J':case 'j':case 'V':case 'v':case 'K':
|
|
||||||
case 'k':case 'W':case 'w':case 'L':case 'X':case 'l':
|
|
||||||
case 'x':case 'Z':case 'z':case 'Y':case 'y':case '_': {
|
|
||||||
t.kind = TK_Identifier;
|
|
||||||
while(lex_is_alphanumeric(lexc(s)) || lexc(s) == '_')
|
|
||||||
lex_advance(s);
|
|
||||||
lex_set_len(s,&t);
|
|
||||||
t.intern_val = intern_string(&array->interns, t.string);
|
|
||||||
if(lex_is_keyword(t.intern_val)){
|
|
||||||
t.kind = TK_Keyword;
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
default: {
|
|
||||||
token_error(&t, lit("Unknown token"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(t.len==0)
|
|
||||||
lex_set_len(s,&t);
|
|
||||||
|
|
||||||
token_array_push(array, &t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
lex_add_stream(Token_Array *array, String stream, String file){
|
|
||||||
Lex_Stream s = {stream, 0, stream.str, file, 0};
|
|
||||||
lex__stream(array, &s);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Token_Array
|
|
||||||
lex_make_token_array(Arena *arena){
|
|
||||||
Token_Array array = token_array_make(arena);
|
|
||||||
init_default_keywords(&array.interns);
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Token_Array
|
|
||||||
lex_stream(Arena *arena, String stream, String file){
|
|
||||||
Token_Array array = lex_make_token_array(arena);
|
|
||||||
lex_add_stream(&array, stream, file);
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
lex_restream(Token_Array *array, String stream, String file){
|
|
||||||
token_array_reset(array);
|
|
||||||
lex_add_stream(array, stream, file);
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
lex_test(){
|
|
||||||
Arena *scratch = arena_begin_scratch();
|
|
||||||
String test = lit("18446744073709551616{})(@?&+-;....->,:::/**/\"Thing\"//R\n Thingy"
|
|
||||||
"\"Test_Meme\"+=-===42524 4294967295 18446744073709551615"
|
|
||||||
"for if while switch :=");
|
|
||||||
Token_Array array = lex_stream(scratch, test, lit("Test1"));
|
|
||||||
|
|
||||||
Token_Kind kind[] = {
|
|
||||||
TK_Error,TK_OpenBrace,TK_CloseBrace,TK_CloseParen,TK_OpenParen,
|
|
||||||
TK_At,TK_Question,TK_BitAnd,TK_Add,TK_Sub,TK_Semicolon,
|
|
||||||
TK_ThreeDots, TK_Dot, TK_Arrow, TK_Comma, TK_DoubleColon, TK_Colon,
|
|
||||||
TK_StringLit, TK_Identifier, TK_StringLit, TK_AddAssign, TK_SubAssign,
|
|
||||||
TK_Equals, TK_Integer, TK_Integer, TK_Integer, TK_Keyword, TK_Keyword,
|
|
||||||
TK_Keyword, TK_Keyword, TK_ColonAssign, TK_End
|
|
||||||
};
|
|
||||||
String strs[] = {
|
|
||||||
lit("18446744073709551616"),lit("{"),lit("}"),lit(")"),lit("("),
|
|
||||||
lit("@"),lit("?"),lit("&"),lit("+"),lit("-"),lit(";"),
|
|
||||||
lit("..."),lit("."),lit("->"),lit(","),lit("::"),lit(":"),
|
|
||||||
lit("Thing"),lit("Thingy"),lit("Test_Meme"), lit("+="),lit("-="),
|
|
||||||
lit("=="),lit("42524"),lit("4294967295"),lit("18446744073709551615"),
|
|
||||||
lit("for"), lit("if"), lit("while"), lit("switch"), lit(":="), lit(""),
|
|
||||||
};
|
|
||||||
U64 vals[] = {
|
|
||||||
42524, 4294967295, 18446744073709551615llu
|
|
||||||
};
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
int ui = 0;
|
|
||||||
for(Token *t = token_array_iter_begin(&array); t->kind != TK_End; t = token_array_iter_next(&array)){
|
|
||||||
assert(t->kind == kind[i]);
|
|
||||||
assert(string_compare(t->string, strs[i++]));
|
|
||||||
if(t->kind == TK_Integer){
|
|
||||||
assert(t->int_val == vals[ui++]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arena_end_scratch();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
// Token metadata
|
|
||||||
//-----------------------------------------------------------------------------
|
|
||||||
global const char *token_kind_string[] = {
|
|
||||||
[TK_End] = "End of stream",
|
|
||||||
[TK_Mul] = "*",
|
|
||||||
[TK_Div] = "/",
|
|
||||||
[TK_Add] = "+",
|
|
||||||
[TK_Sub] = "-",
|
|
||||||
[TK_Mod] = "%",
|
|
||||||
[TK_BitAnd] = "&",
|
|
||||||
[TK_BitOr] = "|",
|
|
||||||
[TK_Pointer] = "^",
|
|
||||||
[TK_Neg] = "~",
|
|
||||||
[TK_Not] = "!",
|
|
||||||
[TK_OpenParen] = "(",
|
|
||||||
[TK_CloseParen] = " ",
|
|
||||||
[TK_OpenBrace] = "{",
|
|
||||||
[TK_CloseBrace] = "}",
|
|
||||||
[TK_OpenBracket] = "[",
|
|
||||||
[TK_CloseBracket] = "]",
|
|
||||||
[TK_Comma] = ",",
|
|
||||||
[TK_Pound] = "#",
|
|
||||||
[TK_Question] = "?",
|
|
||||||
[TK_ThreeDots] = "...",
|
|
||||||
[TK_Semicolon] = ";",
|
|
||||||
[TK_Dot] = ".",
|
|
||||||
[TK_LesserThen] = "<",
|
|
||||||
[TK_GreaterThen] = ">",
|
|
||||||
[TK_Colon] = ":",
|
|
||||||
[TK_Assign] = "=",
|
|
||||||
[TK_ColonAssign] = ":=",
|
|
||||||
[TK_DivAssign] = "/=",
|
|
||||||
[TK_MulAssign] = "*=",
|
|
||||||
[TK_ModAssign] = "%=",
|
|
||||||
[TK_SubAssign] = "-=",
|
|
||||||
[TK_AddAssign] = "+=",
|
|
||||||
[TK_AndAssign] = "&=",
|
|
||||||
[TK_OrAssign] = "|=",
|
|
||||||
[TK_XorAssign] = "^=",
|
|
||||||
[TK_LeftShiftAssign] = "<<=",
|
|
||||||
[TK_RightShiftAssign] = ">>=",
|
|
||||||
[TK_DoubleColon] = "::",
|
|
||||||
[TK_At] = "@",
|
|
||||||
[TK_Decrement] = "--",
|
|
||||||
[TK_Increment] = "++",
|
|
||||||
[TK_PostDecrement] = "--",
|
|
||||||
[TK_PostIncrement] = "++",
|
|
||||||
[TK_LesserThenOrEqual] = "<=",
|
|
||||||
[TK_GreaterThenOrEqual] = ">=",
|
|
||||||
[TK_Equals] = "==",
|
|
||||||
[TK_And] = "&&",
|
|
||||||
[TK_Or] = "||",
|
|
||||||
[TK_NotEquals] = "!=",
|
|
||||||
[TK_LeftShift] = "<<",
|
|
||||||
[TK_RightShift] = ">>",
|
|
||||||
[TK_Arrow] = "->",
|
|
||||||
[TK_ExprSizeof] = "sizeof",
|
|
||||||
[TK_DocComment] = "DocComment",
|
|
||||||
[TK_Comment] = "Comment",
|
|
||||||
[TK_Identifier] = "Identifier",
|
|
||||||
[TK_StringLit] = "StringLit",
|
|
||||||
[TK_Character] = "Character",
|
|
||||||
[TK_Error] = "Error",
|
|
||||||
[TK_Float] = "Float",
|
|
||||||
[TK_Integer] = "Int",
|
|
||||||
[TK_Keyword] = "Keyword",
|
|
||||||
};
|
|
||||||
136
memory.c
136
memory.c
@@ -1,136 +0,0 @@
|
|||||||
global const SizeU default_reserve_size = gib(4);
|
|
||||||
global const SizeU default_alignment = 8;
|
|
||||||
global const SizeU additional_commit_size = mib(1);
|
|
||||||
function SizeU align_up(SizeU size, SizeU align);
|
|
||||||
|
|
||||||
function void
|
|
||||||
memory_copy(void *dst, void *src, SizeU size){
|
|
||||||
U8 *d = (U8*)dst;
|
|
||||||
U8 *s = (U8*)src;
|
|
||||||
for(SizeU i = 0; i < size; i++){
|
|
||||||
d[i] = s[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
memory_zero(void *p, SizeU size){
|
|
||||||
U8 *pp = (U8 *)p;
|
|
||||||
for(SizeU i = 0; i < size; i++)
|
|
||||||
pp[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function int
|
|
||||||
max_int(int a, int b){
|
|
||||||
if(a>b) return a;
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
arena_init(Arena *a){
|
|
||||||
a->memory = os_reserve(default_reserve_size);
|
|
||||||
a->alignment = default_alignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
function void *
|
|
||||||
arena_push_size(Arena *a, SizeU size){
|
|
||||||
SizeU generous_size = size;
|
|
||||||
if(a->memory.commit+generous_size>a->memory.commit){
|
|
||||||
if(a->memory.reserve == 0){
|
|
||||||
arena_init(a);
|
|
||||||
}
|
|
||||||
os_commit(&a->memory, generous_size+additional_commit_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
a->len = align_up(a->len, a->alignment);
|
|
||||||
void *result = (U8*)a->memory.data + a->len;
|
|
||||||
a->len += size;
|
|
||||||
|
|
||||||
memory_zero(result, size);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function void *
|
|
||||||
arena_push_copy(Arena *a, void *pointer, SizeU size){
|
|
||||||
void *result = arena_push_size(a, size);
|
|
||||||
memory_copy(result, pointer, size);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SizeU
|
|
||||||
clamp_top_sizeu(SizeU val, SizeU max){
|
|
||||||
if(val>max)return max;
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
arena_pop_pos(Arena *arena, SizeU pos){
|
|
||||||
pos = clamp_top_sizeu(pos, arena->len);
|
|
||||||
arena->len = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Arena_Checkpoint
|
|
||||||
arena_checkpoint(Arena *arena){
|
|
||||||
Arena_Checkpoint result = {arena, arena->len};
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
arena_restore(Arena_Checkpoint checkpoint){
|
|
||||||
arena_pop_pos(checkpoint.arena, checkpoint.pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
function String
|
|
||||||
arena_push_string_copy(Arena *arena, String string){
|
|
||||||
U8 *copy = arena_push_array(arena, U8, string.len+1);
|
|
||||||
memory_copy(copy, string.str, string.len);
|
|
||||||
copy[string.len] = 0;
|
|
||||||
return (String){copy, string.len};
|
|
||||||
}
|
|
||||||
|
|
||||||
function String
|
|
||||||
string_fmtv(Arena *arena, const char *str, va_list args1) {
|
|
||||||
va_list args2;
|
|
||||||
va_copy(args2, args1);
|
|
||||||
S64 len = vsnprintf(0, 0, str, args2);
|
|
||||||
va_end(args2);
|
|
||||||
|
|
||||||
char *result = (char *)arena_push_size(arena, len + 1);
|
|
||||||
vsnprintf(result, len + 1, str, args1);
|
|
||||||
if (arena->len > 0)
|
|
||||||
arena->len -= 1;
|
|
||||||
|
|
||||||
String res = {(U8 *)result, len};
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define STRING_FMT(arena, str, result) \
|
|
||||||
va_list args1; \
|
|
||||||
va_start(args1, str); \
|
|
||||||
String result = string_fmtv(arena, str, args1); \
|
|
||||||
va_end(args1)
|
|
||||||
|
|
||||||
function String
|
|
||||||
string_fmt(Arena *arena, const char *str, ...) {
|
|
||||||
STRING_FMT(arena, str, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
string_listf(Arena *arena, String_List *list, const char *str, ...){
|
|
||||||
STRING_FMT(arena, str, string);
|
|
||||||
String_Node *node = arena_push_struct(arena, String_Node);
|
|
||||||
node->string = string;
|
|
||||||
SLLQueuePush(list->first, list->last, node);
|
|
||||||
list->char_count += node->string.len;
|
|
||||||
list->node_count += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function String
|
|
||||||
string_list_flatten(Arena *arena, String_List *list){
|
|
||||||
String result = {(U8 *)arena_push_size(arena, list->char_count + 1)};
|
|
||||||
for(String_Node *node = list->first; node; node=node->next){
|
|
||||||
memory_copy(result.str+result.len, node->str, node->len);
|
|
||||||
result.len += node->len;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
20
memory.h
20
memory.h
@@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
typedef struct Arena{
|
|
||||||
OS_Memory memory;
|
|
||||||
U64 len;
|
|
||||||
U64 alignment;
|
|
||||||
}Arena;
|
|
||||||
|
|
||||||
typedef struct Arena_Checkpoint{
|
|
||||||
Arena *arena;
|
|
||||||
SizeU pos;
|
|
||||||
} Arena_Checkpoint;
|
|
||||||
|
|
||||||
function B32 string_compare(String a, String b);
|
|
||||||
function void *arena_push_size(Arena *a, SizeU size);
|
|
||||||
function String arena_push_string_copy(Arena *arena, String string);
|
|
||||||
#define arena_push_array(a,T,c) (T *)arena_push_size(a,sizeof(T)*(c))
|
|
||||||
#define arena_push_struct(a,T) arena_push_array(a,T,1)
|
|
||||||
|
|
||||||
function Arena *arena_begin_scratch();
|
|
||||||
function void arena_end_scratch();
|
|
||||||
11
os.h
11
os.h
@@ -1,11 +0,0 @@
|
|||||||
|
|
||||||
typedef struct OS_Memory{
|
|
||||||
void *data;
|
|
||||||
SizeU commit;
|
|
||||||
SizeU reserve;
|
|
||||||
}OS_Memory;
|
|
||||||
typedef struct Arena Arena;
|
|
||||||
|
|
||||||
function OS_Memory os_reserve(SizeU size);
|
|
||||||
function void os_commit(OS_Memory *m, SizeU size);
|
|
||||||
function String os_read_file(Arena *arena, String file);
|
|
||||||
36
os_win32.c
36
os_win32.c
@@ -1,36 +0,0 @@
|
|||||||
function S32 os_main();
|
|
||||||
const SizeU page_size = 4096;
|
|
||||||
|
|
||||||
function SizeU
|
|
||||||
get_align_offset(SizeU size, SizeU align){
|
|
||||||
SizeU mask = align - 1;
|
|
||||||
SizeU val = size & mask;
|
|
||||||
if(val){
|
|
||||||
val = align - val;
|
|
||||||
}
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SizeU
|
|
||||||
align_up(SizeU size, SizeU align){
|
|
||||||
SizeU result = size + get_align_offset(size, align);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function OS_Memory
|
|
||||||
os_reserve(SizeU size){
|
|
||||||
OS_Memory result = {0};
|
|
||||||
SizeU adjusted_size = align_up(size, page_size);
|
|
||||||
result.data = VirtualAlloc(0, adjusted_size, MEM_RESERVE, PAGE_READWRITE);
|
|
||||||
assert_msg(result.data, "Failed to reserve virtual memory");
|
|
||||||
result.reserve = adjusted_size;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
os_commit(OS_Memory *m, SizeU size){
|
|
||||||
SizeU commit = align_up(size, page_size);
|
|
||||||
void *p = VirtualAlloc((U8 *)m->data + m->commit, commit, MEM_COMMIT, PAGE_READWRITE);
|
|
||||||
assert_msg(p, "Failed to commit more memory");
|
|
||||||
m->commit += commit;
|
|
||||||
}
|
|
||||||
365
print.c
365
print.c
@@ -1,365 +0,0 @@
|
|||||||
function void expr_print(Expr *expr);
|
|
||||||
function void typespec_print(Typespec *spec);
|
|
||||||
|
|
||||||
global S64 indent;
|
|
||||||
|
|
||||||
#define println(...) do{ printf("\n"); print_indent(); printf(__VA_ARGS__); }while(0)
|
|
||||||
|
|
||||||
|
|
||||||
function void
|
|
||||||
print_indent(){
|
|
||||||
for(S64 i = 0; i < indent*2; i++)
|
|
||||||
printf(" ");
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
token_print(Token *token){
|
|
||||||
printf("%.*s", (S32)token->len, token->str);
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
expr_compound_print(Expr_Compound_Field *field){
|
|
||||||
switch(field->kind){
|
|
||||||
case COMPOUND_Default: {
|
|
||||||
expr_print(field->init);
|
|
||||||
}break;
|
|
||||||
case COMPOUND_Named: {
|
|
||||||
printf(".%s = ", field->name.s.str);
|
|
||||||
expr_print(field->init);
|
|
||||||
}break;
|
|
||||||
case COMPOUND_Index: {
|
|
||||||
printf("[");
|
|
||||||
expr_print(field->index);
|
|
||||||
printf("] = ");
|
|
||||||
expr_print(field->init);
|
|
||||||
}break;
|
|
||||||
default: invalid_codepath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
expr_print(Expr *expr){
|
|
||||||
switch(expr->kind) {
|
|
||||||
case EK_Int:case EK_String:case EK_Identifier: {
|
|
||||||
token_print(expr->token);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case EK_SizeExpr:{
|
|
||||||
printf("sizeof(");
|
|
||||||
expr_print(expr->size_expr.expr);
|
|
||||||
printf(")");
|
|
||||||
}break;
|
|
||||||
|
|
||||||
case EK_Compound:{
|
|
||||||
if(expr->compound.typespec){
|
|
||||||
printf("(");
|
|
||||||
typespec_print(expr->compound.typespec);
|
|
||||||
printf(")");
|
|
||||||
}
|
|
||||||
printf("{");
|
|
||||||
for(Expr_Compound_Field *n = expr->compound.first; n; n=n->next){
|
|
||||||
expr_compound_print(n);
|
|
||||||
if(n!=expr->compound.last) printf(",");
|
|
||||||
}
|
|
||||||
printf("}");
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case EK_SizeType:{
|
|
||||||
printf("sizeof(");
|
|
||||||
typespec_print(expr->size_type.typespec);
|
|
||||||
printf(")");
|
|
||||||
}break;
|
|
||||||
|
|
||||||
case EK_Paren:{
|
|
||||||
printf("(");
|
|
||||||
expr_print(expr->paren.expr);
|
|
||||||
printf(")");
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case EK_Field:{
|
|
||||||
expr_print(expr->field.expr);
|
|
||||||
printf(".%s", expr->field.name.s.str);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case EK_Binary:{
|
|
||||||
printf("(");
|
|
||||||
expr_print(expr->binary.left);
|
|
||||||
printf("%s", token_kind_string[expr->binary.op]);
|
|
||||||
expr_print(expr->binary.right);
|
|
||||||
printf(")");
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case EK_PostfixUnary:{
|
|
||||||
printf("(");
|
|
||||||
expr_print(expr->unary.expr);
|
|
||||||
token_print(expr->token);
|
|
||||||
printf(")");
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case EK_Unary:{
|
|
||||||
printf("(");
|
|
||||||
token_print(expr->token);
|
|
||||||
expr_print(expr->unary.expr);
|
|
||||||
printf(")");
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case EK_Ternary:{
|
|
||||||
printf("(");
|
|
||||||
expr_print(expr->ternary.cond);
|
|
||||||
printf("?");
|
|
||||||
expr_print(expr->ternary.on_true);
|
|
||||||
printf(":");
|
|
||||||
expr_print(expr->ternary.on_false);
|
|
||||||
printf(")");
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case EK_Cast:{
|
|
||||||
printf("(");
|
|
||||||
printf("(");
|
|
||||||
typespec_print(expr->cast.typespec);
|
|
||||||
printf(")");
|
|
||||||
expr_print(expr->cast.expr);
|
|
||||||
printf(")");
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case EK_Index:{
|
|
||||||
expr_print(expr->index.atom);
|
|
||||||
printf("[");
|
|
||||||
expr_print(expr->index.index);
|
|
||||||
printf("]");
|
|
||||||
}break;
|
|
||||||
|
|
||||||
case EK_Call:{
|
|
||||||
expr_print(expr->call.atom);
|
|
||||||
printf("(");
|
|
||||||
for(Expr_Compound_Field *n = expr->call.first; n; n=n->next){
|
|
||||||
expr_compound_print(n);
|
|
||||||
if(n!=expr->call.last) printf(",");
|
|
||||||
}
|
|
||||||
printf(")");
|
|
||||||
}break;
|
|
||||||
default: {invalid_codepath;} break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
typespec_print(Typespec *spec){
|
|
||||||
switch(spec->kind) {
|
|
||||||
case TS_Name: {
|
|
||||||
printf("%s", spec->name.s.str);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case TS_NamedArgument: {
|
|
||||||
printf("%s: ", spec->named.name.s.str);
|
|
||||||
typespec_print(spec->named.base);
|
|
||||||
}break;
|
|
||||||
|
|
||||||
case TS_Pointer: {
|
|
||||||
typespec_print(spec->base);
|
|
||||||
printf("*");
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case TS_Array: {
|
|
||||||
typespec_print(spec->arr.base);
|
|
||||||
printf("[");
|
|
||||||
expr_print(spec->arr.size);
|
|
||||||
printf("]");
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case TS_Function: {
|
|
||||||
printf("(");
|
|
||||||
for(Typespec *n = spec->func.first; n; n=n->next){
|
|
||||||
typespec_print(n);
|
|
||||||
if(n!=spec->func.last)
|
|
||||||
printf(", ");
|
|
||||||
}
|
|
||||||
|
|
||||||
printf(")");
|
|
||||||
typespec_print(spec->func.ret);
|
|
||||||
} break;
|
|
||||||
default: {invalid_codepath;} break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
print_assign_expr(Expr *expr){
|
|
||||||
if(expr){
|
|
||||||
printf(" = ");
|
|
||||||
expr_print(expr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function void
|
|
||||||
ast_print(AST *in){
|
|
||||||
switch(in->kind) {
|
|
||||||
case AST_Program: {
|
|
||||||
Program *node = (Program *)in;
|
|
||||||
for(AST *n = node->first; n; n=n->next){
|
|
||||||
ast_print(n);
|
|
||||||
printf("\n");
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case AST_Decl_Const:
|
|
||||||
case AST_Decl_Var:{
|
|
||||||
Decl_Var *node = (Decl_Var *)in;
|
|
||||||
println("");
|
|
||||||
if(node->typespec) typespec_print(node->typespec);
|
|
||||||
printf(" %s", node->name.s.str);
|
|
||||||
print_assign_expr(node->expr);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case AST_Decl_Typedef:{
|
|
||||||
Decl_Typedef *node = (Decl_Typedef *)in;
|
|
||||||
println("typedef %s ", node->name.s.str);
|
|
||||||
typespec_print(node->typespec);
|
|
||||||
printf(";");
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case AST_Decl_Func:{
|
|
||||||
Decl_Func *node = (Decl_Func *)in;
|
|
||||||
println("function ");
|
|
||||||
typespec_print(node->ret);
|
|
||||||
printf("\n%s", node->name.s.str);
|
|
||||||
printf("(");
|
|
||||||
AST_IterT(node, arg, Decl_Func_Arg){
|
|
||||||
typespec_print(arg->typespec);
|
|
||||||
printf(" %s", arg->name.s.str);
|
|
||||||
if((AST *)arg != node->last)
|
|
||||||
printf(", ");
|
|
||||||
}
|
|
||||||
printf(")");
|
|
||||||
if(!node->is_incomplete){
|
|
||||||
ast_print((AST *)node->body);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case AST_Decl_Struct:
|
|
||||||
case AST_Decl_Union :{
|
|
||||||
Decl_Struct *node = (Decl_Struct *)in;
|
|
||||||
const char *struct_name = node->kind==AST_Decl_Struct ? "struct" : "union";
|
|
||||||
println("%s %s{", struct_name, node->name.s.str?(char*)node->name.s.str:"");
|
|
||||||
indent++;
|
|
||||||
for(AST *n = node->first; n; n=n->next){
|
|
||||||
ast_print(n);
|
|
||||||
printf(";");
|
|
||||||
}
|
|
||||||
indent--;
|
|
||||||
println("};");
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case AST_Decl_SubStruct:
|
|
||||||
case AST_Decl_SubUnion :{
|
|
||||||
Decl_Struct *node = (Decl_Struct *)in;
|
|
||||||
const char *struct_name = node->kind==AST_Decl_Struct ? "struct" : "union";
|
|
||||||
println("%s %s{", struct_name, node->name.s.str?(char*)node->name.s.str:"");
|
|
||||||
indent++;
|
|
||||||
for(AST *n = node->first; n; n=n->next){
|
|
||||||
ast_print(n);
|
|
||||||
}
|
|
||||||
indent--;
|
|
||||||
println("};");
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case AST_Decl_Enum:{
|
|
||||||
Decl_Enum *node = (Decl_Enum *)in;
|
|
||||||
println("enum %s : ", node->name.s.str);
|
|
||||||
typespec_print(node->typespec);
|
|
||||||
printf("{");
|
|
||||||
indent++;
|
|
||||||
AST_IterT(node, n, Decl_Enum_Child){
|
|
||||||
//print_notes(p, n->first_note);
|
|
||||||
println("%s", n->name.s.str);
|
|
||||||
print_assign_expr(n->expr);
|
|
||||||
printf(",");
|
|
||||||
}
|
|
||||||
indent--;
|
|
||||||
println("};");
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case AST_Stmt_Init:{
|
|
||||||
Stmt_Init *node = (Stmt_Init *)in;
|
|
||||||
if(node->typespec) typespec_print(node->typespec);
|
|
||||||
printf(" %s", node->name.s.str);
|
|
||||||
print_assign_expr(node->expr);
|
|
||||||
}break;
|
|
||||||
|
|
||||||
case AST_Stmt_Assign:{
|
|
||||||
Stmt_Assign *node = (Stmt_Assign *)in;
|
|
||||||
expr_print(node->left);
|
|
||||||
token_print(node->pos);
|
|
||||||
expr_print(node->right);
|
|
||||||
}break;
|
|
||||||
|
|
||||||
case AST_Stmt_Expr:{
|
|
||||||
Stmt_Expr *node = (Stmt_Expr *)in;
|
|
||||||
expr_print(node->expr);
|
|
||||||
}break;
|
|
||||||
|
|
||||||
case AST_Stmt_Defer:{
|
|
||||||
Stmt_Defer *node = (Stmt_Defer *)in;
|
|
||||||
printf("defer ");
|
|
||||||
ast_print((AST *)node->body);
|
|
||||||
}break;
|
|
||||||
|
|
||||||
case AST_Stmt_Return:{
|
|
||||||
Stmt_Return *node = (Stmt_Return *)in;
|
|
||||||
printf("return ");
|
|
||||||
expr_print(node->expr);
|
|
||||||
}break;
|
|
||||||
|
|
||||||
case AST_Stmt_Block:{
|
|
||||||
Stmt_Block *node = (Stmt_Block *)in;
|
|
||||||
printf(" {");
|
|
||||||
indent++;
|
|
||||||
AST_IterT(node, n, AST){
|
|
||||||
println("");
|
|
||||||
ast_print(n);
|
|
||||||
if(n->kind != AST_Stmt_If && n->kind != AST_Stmt_For && n->kind != AST_Stmt_Else &&
|
|
||||||
n->kind != AST_Stmt_ElseIf)
|
|
||||||
printf(";");
|
|
||||||
}
|
|
||||||
indent--;
|
|
||||||
println("}");
|
|
||||||
}break;
|
|
||||||
|
|
||||||
|
|
||||||
case AST_Stmt_Else:{
|
|
||||||
Stmt_Else *node = (Stmt_Else *)in;
|
|
||||||
printf("else ");
|
|
||||||
ast_print((AST *)node->body);
|
|
||||||
}break;
|
|
||||||
|
|
||||||
case AST_Stmt_ElseIf:{
|
|
||||||
Stmt_ElseIf *node = (Stmt_ElseIf *)in;
|
|
||||||
printf("else if ");
|
|
||||||
expr_print(node->condition);
|
|
||||||
ast_print((AST *)node->body);
|
|
||||||
}break;
|
|
||||||
|
|
||||||
case AST_Stmt_If:{
|
|
||||||
Stmt_If *node = (Stmt_If *)in;
|
|
||||||
printf("if ");
|
|
||||||
expr_print(node->condition);
|
|
||||||
ast_print((AST *)node->body);
|
|
||||||
AST_IterT(node, n, AST){
|
|
||||||
ast_print(n);
|
|
||||||
}
|
|
||||||
}break;
|
|
||||||
|
|
||||||
case AST_Stmt_For:{
|
|
||||||
Stmt_For *node = (Stmt_For *)in;
|
|
||||||
printf("for(");
|
|
||||||
if(node->on_begin) ast_print(node->on_begin);
|
|
||||||
printf(";");
|
|
||||||
if(node->condition) expr_print(node->condition);
|
|
||||||
printf(";");
|
|
||||||
if(node->on_iter) ast_print(node->on_iter);
|
|
||||||
printf(")");
|
|
||||||
ast_print((AST *)node->body);
|
|
||||||
}break;
|
|
||||||
|
|
||||||
default: {invalid_codepath;} break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Reference in New Issue
Block a user