Change extensions .cpp to .c

This commit is contained in:
Ginger Bill
2016-11-23 12:29:50 +00:00
parent a77c6b3e55
commit 4d30ef7eda
21 changed files with 1 additions and 23843 deletions

View File

@@ -46,7 +46,7 @@ rem pushd %build_dir%
del *.pdb > NUL 2> NUL
del *.ilk > NUL 2> NUL
cl %compiler_settings% "src\main.cpp" ^
cl %compiler_settings% "src\main.c" ^
/link %linker_settings% -OUT:%exe_name% ^
&& odin run code/demo.odin
rem odin run code/demo.odin

View File

@@ -1,235 +0,0 @@
#define ARRAY_GROW_FORMULA(x) (2*(x) + 8)
GB_STATIC_ASSERT(ARRAY_GROW_FORMULA(0) > 0);
#define Array(Type_) struct { \
gbAllocator allocator; \
Type_ * e; \
isize count; \
isize capacity; \
}
typedef Array(void) ArrayVoid;
#define array_init_reserve(x_, allocator_, init_capacity_) do { \
GB_ASSERT((x_) != NULL); \
void **e = cast(void **)&((x_)->e); \
(x_)->allocator = (allocator_); \
(x_)->count = 0; \
(x_)->capacity = (init_capacity_); \
*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_capacity_)); \
} while (0)
#define array_init_count(x_, allocator_, init_count_) do { \
GB_ASSERT((x_) != NULL); \
void **e = cast(void **)&((x_)->e); \
(x_)->allocator = (allocator_); \
(x_)->count = (init_count_); \
(x_)->capacity = (init_count_); \
*e = gb_alloc((allocator_), gb_size_of(*(x_)->e)*(init_count_)); \
} while (0)
#define array_init(x_, allocator_) do { array_init_reserve(x_, allocator_, ARRAY_GROW_FORMULA(0)); } while (0)
#define array_free(x_) do { gb_free((x_)->allocator, (x_)->e); } while (0)
#define array_set_capacity(x_, capacity_) do { array__set_capacity((x_), (capacity_), gb_size_of(*(x_)->e)); } while (0)
#define array_grow(x_, min_capacity_) do { \
isize new_capacity = ARRAY_GROW_FORMULA((x_)->capacity); \
if (new_capacity < (min_capacity_)) { \
new_capacity = (min_capacity_); \
} \
array_set_capacity(x_, new_capacity); \
} while (0)
#define array_add(x_, item_) do { \
if ((x_)->capacity < (x_)->count+1) { \
array_grow(x_, 0); \
} \
(x_)->e[(x_)->count++] = item_; \
} while (0)
#define array_pop(x_) do { GB_ASSERT((x_)->count > 0); (x_)->count--; } while (0)
#define array_clear(x_) do { (x_)->count = 0; } while (0)
#define array_resize(x_, new_count_) do { \
if ((x_)->capacity < (new_count_)) { \
array_grow((x_), (new_count_)); \
} \
(x_)->count = (new_count_); \
} while (0)
#define array_reserve(x_, new_capacity_) do { \
if ((x_)->capacity < (new_capacity_)) { \
array_set_capacity((x_), (new_capacity_)); \
} \
} while (0)
void array__set_capacity(void *ptr, isize capacity, isize element_size) {
GB_ASSERT(ptr != NULL);
ArrayVoid *x = cast(ArrayVoid *)ptr;
GB_ASSERT(element_size > 0);
if (capacity == x->capacity) {
return;
}
if (capacity < x->count) {
if (x->capacity < capacity) {
isize new_capacity = ARRAY_GROW_FORMULA(x->capacity);
if (new_capacity < capacity) {
new_capacity = capacity;
}
array__set_capacity(ptr, new_capacity, element_size);
}
x->count = capacity;
}
{
// TODO(bill): Resize rather than copy and delete
void *new_data = gb_alloc(x->allocator, element_size*capacity);
gb_memmove(new_data, x->e, element_size*x->count);
gb_free(x->allocator, x->e);
x->capacity = capacity;
x->e = new_data;
}
}
#if 0
template <typename T>
struct Array {
gbAllocator allocator;
T * data;
isize count;
isize capacity;
T &operator[](isize index) {
GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");
return data[index];
}
T const &operator[](isize index) const {
GB_ASSERT_MSG(0 <= index && index < count, "Index out of bounds");
return data[index];
}
};
template <typename T> void array_init (Array<T> *array, gbAllocator a, isize init_capacity = ARRAY_GROW_FORMULA(0));
template <typename T> void array_init_count (Array<T> *array, gbAllocator a, isize count);
template <typename T> Array<T> array_make (T *data, isize count, isize capacity);
template <typename T> void array_free (Array<T> *array);
template <typename T> void array_add (Array<T> *array, T const &t);
template <typename T> T array_pop (Array<T> *array);
template <typename T> void array_clear (Array<T> *array);
template <typename T> void array_reserve (Array<T> *array, isize capacity);
template <typename T> void array_resize (Array<T> *array, isize count);
template <typename T> void array_set_capacity(Array<T> *array, isize capacity);
template <typename T>
void array_init(Array<T> *array, gbAllocator a, isize init_capacity) {
array->allocator = a;
array->data = gb_alloc_array(a, T, init_capacity);
array->count = 0;
array->capacity = init_capacity;
}
template <typename T>
void array_init_count(Array<T> *array, gbAllocator a, isize count) {
array->allocator = a;
array->data = gb_alloc_array(a, T, count);
array->count = count;
array->capacity = count;
}
template <typename T>
Array<T> array_make(T *data, isize count, isize capacity) {
Array<T> a = {0};
a.data = data;
a.count = count;
a.capacity = capacity;
return a;
}
template <typename T>
void array_free(Array<T> *array) {
if (array->allocator.proc != NULL) {
gb_free(array->allocator, array->data);
}
array->count = 0;
array->capacity = 0;
}
template <typename T>
void array__grow(Array<T> *array, isize min_capacity) {
isize new_capacity = ARRAY_GROW_FORMULA(array->capacity);
if (new_capacity < min_capacity) {
new_capacity = min_capacity;
}
array_set_capacity(array, new_capacity);
}
template <typename T>
void array_add(Array<T> *array, T const &t) {
if (array->capacity < array->count+1) {
array__grow(array, 0);
}
array->data[array->count] = t;
array->count++;
}
template <typename T>
T array_pop(Array<T> *array) {
GB_ASSERT(array->count > 0);
array->count--;
return array->data[array->count];
}
template <typename T>
void array_clear(Array<T> *array) {
array->count = 0;
}
template <typename T>
void array_reserve(Array<T> *array, isize capacity) {
if (array->capacity < capacity) {
array_set_capacity(array, capacity);
}
}
template <typename T>
void array_resize(Array<T> *array, isize count) {
if (array->capacity < count) {
array__grow(array, count);
}
array->count = count;
}
template <typename T>
void array_set_capacity(Array<T> *array, isize capacity) {
if (capacity == array->capacity) {
return;
}
if (capacity < array->count) {
array_resize(array, capacity);
}
T *new_data = NULL;
if (capacity > 0) {
new_data = gb_alloc_array(array->allocator, T, capacity);
gb_memmove(new_data, array->data, gb_size_of(T) * array->capacity);
}
gb_free(array->allocator, array->data);
array->data = new_data;
array->capacity = capacity;
}
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,545 +0,0 @@
bool check_is_terminating(AstNode *node);
void check_stmt (Checker *c, AstNode *node, u32 flags);
void check_stmt_list (Checker *c, AstNodeArray stmts, u32 flags);
void check_type_decl (Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker);
void check_const_decl (Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr);
void check_proc_decl (Checker *c, Entity *e, DeclInfo *d);
void check_var_decl (Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr);
// NOTE(bill): `content_name` is for debugging and error messages
Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) {
if (operand->mode == Addressing_Invalid ||
operand->type == t_invalid ||
e->type == t_invalid) {
if (operand->mode == Addressing_Builtin) {
gbString expr_str = expr_to_string(operand->expr);
// TODO(bill): is this a good enough error message?
error(ast_node_token(operand->expr),
"Cannot assign builtin procedure `%s` in %.*s",
expr_str,
LIT(context_name));
operand->mode = Addressing_Invalid;
gb_string_free(expr_str);
}
if (e->type == NULL) {
e->type = t_invalid;
}
return NULL;
}
if (e->type == NULL) {
// NOTE(bill): Use the type of the operand
Type *t = operand->type;
if (is_type_untyped(t)) {
if (t == t_invalid || is_type_untyped_nil(t)) {
error(e->token, "Use of untyped nil in %.*s", LIT(context_name));
e->type = t_invalid;
return NULL;
}
t = default_type(t);
}
e->type = t;
}
check_assignment(c, operand, e->type, context_name);
if (operand->mode == Addressing_Invalid) {
return NULL;
}
return e->type;
}
void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNodeArray inits, String context_name) {
if ((lhs == NULL || lhs_count == 0) && inits.count == 0) {
return;
}
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
// NOTE(bill): If there is a bad syntax error, rhs > lhs which would mean there would need to be
// an extra allocation
Array(Operand) operands;
array_init_reserve(&operands, c->tmp_allocator, 2*lhs_count);
for_array(i, inits) {
AstNode *rhs = inits.e[i];
Operand o = {0};
check_multi_expr(c, &o, rhs);
if (o.type->kind != Type_Tuple) {
array_add(&operands, o);
} else {
TypeTuple *tuple = &o.type->Tuple;
for (isize j = 0; j < tuple->variable_count; j++) {
o.type = tuple->variables[j]->type;
array_add(&operands, o);
}
}
}
isize rhs_count = operands.count;
for_array(i, operands) {
if (operands.e[i].mode == Addressing_Invalid) {
rhs_count--;
}
}
isize max = gb_min(lhs_count, rhs_count);
for (isize i = 0; i < max; i++) {
check_init_variable(c, lhs[i], &operands.e[i], context_name);
}
if (rhs_count > 0 && lhs_count != rhs_count) {
error(lhs[0]->token, "Assignment count mismatch `%td` := `%td`", lhs_count, rhs_count);
}
gb_temp_arena_memory_end(tmp);
}
void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, CycleChecker *cycle_checker) {
if (e->type != NULL) {
return;
}
if (d == NULL) {
DeclInfo **found = map_decl_info_get(&c->info.entities, hash_pointer(e));
if (found) {
d = *found;
} else {
e->type = t_invalid;
set_base_type(named_type, t_invalid);
return;
// GB_PANIC("`%.*s` should been declared!", LIT(e->token.string));
}
}
if (e->kind == Entity_Procedure) {
check_proc_decl(c, e, d);
return;
}
CheckerContext prev = c->context;
c->context.scope = d->scope;
c->context.decl = d;
switch (e->kind) {
case Entity_Constant:
check_const_decl(c, e, d->type_expr, d->init_expr);
break;
case Entity_Variable:
check_var_decl(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr);
break;
case Entity_TypeName:
check_type_decl(c, e, d->type_expr, named_type, cycle_checker);
break;
}
c->context = prev;
}
void check_var_decl_node(Checker *c, AstNode *node) {
ast_node(vd, VarDecl, node);
isize entity_count = vd->names.count;
isize entity_index = 0;
Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
for_array(i, vd->names) {
AstNode *name = vd->names.e[i];
Entity *entity = NULL;
if (name->kind == AstNode_Ident) {
Token token = name->Ident;
String str = token.string;
Entity *found = NULL;
// NOTE(bill): Ignore assignments to `_`
if (str_ne(str, str_lit("_"))) {
found = current_scope_lookup_entity(c->context.scope, str);
}
if (found == NULL) {
entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
add_entity_definition(&c->info, name, entity);
} else {
TokenPos pos = found->token.pos;
error(token,
"Redeclaration of `%.*s` in this scope\n"
"\tat %.*s(%td:%td)",
LIT(str), LIT(pos.file), pos.line, pos.column);
entity = found;
}
} else {
error(ast_node_token(name), "A variable declaration must be an identifier");
}
if (entity == NULL) {
entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
}
entities[entity_index++] = entity;
}
Type *init_type = NULL;
if (vd->type) {
init_type = check_type_extra(c, vd->type, NULL, NULL);
if (init_type == NULL)
init_type = t_invalid;
}
for (isize i = 0; i < entity_count; i++) {
Entity *e = entities[i];
GB_ASSERT(e != NULL);
if (e->flags & EntityFlag_Visited) {
e->type = t_invalid;
continue;
}
e->flags |= EntityFlag_Visited;
if (e->type == NULL)
e->type = init_type;
}
check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
for_array(i, vd->names) {
if (entities[i] != NULL) {
add_entity(c, c->context.scope, vd->names.e[i], entities[i]);
}
}
}
void check_init_constant(Checker *c, Entity *e, Operand *operand) {
if (operand->mode == Addressing_Invalid ||
operand->type == t_invalid ||
e->type == t_invalid) {
if (e->type == NULL) {
e->type = t_invalid;
}
return;
}
if (operand->mode != Addressing_Constant) {
// TODO(bill): better error
error(ast_node_token(operand->expr),
"`%.*s` is not a constant", LIT(ast_node_token(operand->expr).string));
if (e->type == NULL) {
e->type = t_invalid;
}
return;
}
// if (!is_type_constant_type(operand->type)) {
// gbString type_str = type_to_string(operand->type);
// defer (gb_string_free(type_str));
// error(ast_node_token(operand->expr),
// "Invalid constant type: `%s`", type_str);
// if (e->type == NULL) {
// e->type = t_invalid;
// }
// return;
// }
if (e->type == NULL) { // NOTE(bill): type inference
e->type = operand->type;
}
check_assignment(c, operand, e->type, str_lit("constant declaration"));
if (operand->mode == Addressing_Invalid) {
return;
}
e->Constant.value = operand->value;
}
void check_const_decl(Checker *c, Entity *e, AstNode *type_expr, AstNode *init_expr) {
GB_ASSERT(e->type == NULL);
if (e->flags & EntityFlag_Visited) {
e->type = t_invalid;
return;
}
e->flags |= EntityFlag_Visited;
if (type_expr) {
Type *t = check_type(c, type_expr);
// if (!is_type_constant_type(t)) {
// gbString str = type_to_string(t);
// defer (gb_string_free(str));
// error(ast_node_token(type_expr),
// "Invalid constant type `%s`", str);
// e->type = t_invalid;
// return;
// }
e->type = t;
}
Operand operand = {0};
if (init_expr) {
check_expr(c, &operand, init_expr);
}
check_init_constant(c, e, &operand);
}
void check_type_decl(Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker) {
GB_ASSERT(e->type == NULL);
Type *named = make_type_named(c->allocator, e->token.string, NULL, e);
named->Named.type_name = e;
if (def != NULL && def->kind == Type_Named) {
def->Named.base = named;
}
e->type = named;
CycleChecker local_cycle_checker = {0};
if (cycle_checker == NULL) {
cycle_checker = &local_cycle_checker;
}
Type *bt = check_type_extra(c, type_expr, named, cycle_checker_add(cycle_checker, e));
named->Named.base = bt;
named->Named.base = base_type(named->Named.base);
if (named->Named.base == t_invalid) {
gb_printf("check_type_decl: %s\n", type_to_string(named));
}
cycle_checker_destroy(&local_cycle_checker);
}
bool are_signatures_similar_enough(Type *a_, Type *b_) {
GB_ASSERT(a_->kind == Type_Proc);
GB_ASSERT(b_->kind == Type_Proc);
TypeProc *a = &a_->Proc;
TypeProc *b = &b_->Proc;
if (a->param_count != b->param_count) {
return false;
}
if (a->result_count != b->result_count) {
return false;
}
for (isize i = 0; i < a->param_count; i++) {
Type *x = base_type(a->params->Tuple.variables[i]->type);
Type *y = base_type(b->params->Tuple.variables[i]->type);
if (is_type_pointer(x) && is_type_pointer(y)) {
continue;
}
if (!are_types_identical(x, y)) {
return false;
}
}
for (isize i = 0; i < a->result_count; i++) {
Type *x = base_type(a->results->Tuple.variables[i]->type);
Type *y = base_type(b->results->Tuple.variables[i]->type);
if (is_type_pointer(x) && is_type_pointer(y)) {
continue;
}
if (!are_types_identical(x, y)) {
return false;
}
}
return true;
}
void check_proc_decl(Checker *c, Entity *e, DeclInfo *d) {
GB_ASSERT(e->type == NULL);
Type *proc_type = make_type_proc(c->allocator, e->scope, NULL, 0, NULL, 0, false);
e->type = proc_type;
ast_node(pd, ProcDecl, d->proc_decl);
check_open_scope(c, pd->type);
check_procedure_type(c, proc_type, pd->type);
bool is_foreign = (pd->tags & ProcTag_foreign) != 0;
bool is_link_name = (pd->tags & ProcTag_link_name) != 0;
bool is_inline = (pd->tags & ProcTag_inline) != 0;
bool is_no_inline = (pd->tags & ProcTag_no_inline) != 0;
if ((d->scope->is_file || d->scope->is_global) &&
str_eq(e->token.string, str_lit("main"))) {
if (proc_type != NULL) {
TypeProc *pt = &proc_type->Proc;
if (pt->param_count != 0 ||
pt->result_count) {
gbString str = type_to_string(proc_type);
error(e->token,
"Procedure type of `main` was expected to be `proc()`, got %s", str);
gb_string_free(str);
}
}
}
if (is_inline && is_no_inline) {
error(ast_node_token(pd->type),
"You cannot apply both `inline` and `no_inline` to a procedure");
}
if (is_foreign && is_link_name) {
error(ast_node_token(pd->type),
"You cannot apply both `foreign` and `link_name` to a procedure");
}
if (pd->body != NULL) {
if (is_foreign) {
error(ast_node_token(pd->body),
"A procedure tagged as `#foreign` cannot have a body");
}
d->scope = c->context.scope;
GB_ASSERT(pd->body->kind == AstNode_BlockStmt);
check_procedure_later(c, c->curr_ast_file, e->token, d, proc_type, pd->body, pd->tags);
}
if (is_foreign) {
MapEntity *fp = &c->info.foreign_procs;
AstNodeProcDecl *proc_decl = &d->proc_decl->ProcDecl;
String name = proc_decl->name->Ident.string;
if (proc_decl->foreign_name.len > 0) {
name = proc_decl->foreign_name;
}
HashKey key = hash_string(name);
Entity **found = map_entity_get(fp, key);
if (found) {
Entity *f = *found;
TokenPos pos = f->token.pos;
Type *this_type = base_type(e->type);
Type *other_type = base_type(f->type);
if (!are_signatures_similar_enough(this_type, other_type)) {
error(ast_node_token(d->proc_decl),
"Redeclaration of #foreign procedure `%.*s` with different type signatures\n"
"\tat %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column);
}
} else {
map_entity_set(fp, key, e);
}
} else if (is_link_name) {
MapEntity *fp = &c->info.foreign_procs;
AstNodeProcDecl *proc_decl = &d->proc_decl->ProcDecl;
String name = proc_decl->link_name;
HashKey key = hash_string(name);
Entity **found = map_entity_get(fp, key);
if (found) {
Entity *f = *found;
TokenPos pos = f->token.pos;
error(ast_node_token(d->proc_decl),
"Non unique #link_name for procedure `%.*s`\n"
"\tother at %.*s(%td:%td)",
LIT(name), LIT(pos.file), pos.line, pos.column);
} else {
map_entity_set(fp, key, e);
}
}
check_close_scope(c);
}
void check_var_decl(Checker *c, Entity *e, Entity **entities, isize entity_count, AstNode *type_expr, AstNode *init_expr) {
GB_ASSERT(e->type == NULL);
GB_ASSERT(e->kind == Entity_Variable);
if (e->flags & EntityFlag_Visited) {
e->type = t_invalid;
return;
}
e->flags |= EntityFlag_Visited;
if (type_expr != NULL)
e->type = check_type_extra(c, type_expr, NULL, NULL);
if (init_expr == NULL) {
if (type_expr == NULL)
e->type = t_invalid;
return;
}
if (entities == NULL || entity_count == 1) {
GB_ASSERT(entities == NULL || entities[0] == e);
Operand operand = {0};
check_expr(c, &operand, init_expr);
check_init_variable(c, e, &operand, str_lit("variable declaration"));
}
if (type_expr != NULL) {
for (isize i = 0; i < entity_count; i++)
entities[i]->type = e->type;
}
AstNodeArray inits;
array_init_reserve(&inits, c->allocator, 1);
array_add(&inits, init_expr);
check_init_variables(c, entities, entity_count, inits, str_lit("variable declaration"));
}
void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body) {
GB_ASSERT(body->kind == AstNode_BlockStmt);
CheckerContext old_context = c->context;
c->context.scope = decl->scope;
c->context.decl = decl;
GB_ASSERT(type->kind == Type_Proc);
if (type->Proc.param_count > 0) {
TypeTuple *params = &type->Proc.params->Tuple;
for (isize i = 0; i < params->variable_count; i++) {
Entity *e = params->variables[i];
GB_ASSERT(e->kind == Entity_Variable);
if (!(e->flags & EntityFlag_Anonymous)) {
continue;
}
String name = e->token.string;
Type *t = base_type(type_deref(e->type));
if (is_type_struct(t) || is_type_raw_union(t)) {
Scope **found = map_scope_get(&c->info.scopes, hash_pointer(t->Record.node));
GB_ASSERT(found != NULL);
for_array(i, (*found)->elements.entries) {
Entity *f = (*found)->elements.entries.e[i].value;
if (f->kind == Entity_Variable) {
Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
Entity *prev = scope_insert_entity(c->context.scope, uvar);
if (prev != NULL) {
error(e->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
break;
}
}
}
} else {
error(e->token, "`using` can only be applied to variables of type struct or raw_union");
break;
}
}
}
push_procedure(c, type);
{
ast_node(bs, BlockStmt, body);
// TODO(bill): Check declarations first (except mutable variable declarations)
check_stmt_list(c, bs->stmts, 0);
if (type->Proc.result_count > 0) {
if (!check_is_terminating(body)) {
error(bs->close, "Missing return statement at the end of the procedure");
}
}
}
pop_procedure(c);
check_scope_usage(c, c->context.scope);
c->context = old_context;
}

View File

@@ -1,193 +0,0 @@
typedef struct Scope Scope;
typedef struct Checker Checker;
typedef struct Type Type;
typedef enum BuiltinProcId BuiltinProcId;
typedef enum ImplicitValueId ImplicitValueId;
#define ENTITY_KINDS \
ENTITY_KIND(Invalid) \
ENTITY_KIND(Constant) \
ENTITY_KIND(Variable) \
ENTITY_KIND(TypeName) \
ENTITY_KIND(Procedure) \
ENTITY_KIND(Builtin) \
ENTITY_KIND(ImportName) \
ENTITY_KIND(Nil) \
ENTITY_KIND(ImplicitValue) \
ENTITY_KIND(Count)
typedef enum EntityKind {
#define ENTITY_KIND(k) GB_JOIN2(Entity_, k),
ENTITY_KINDS
#undef ENTITY_KIND
} EntityKind;
String const entity_strings[] = {
#define ENTITY_KIND(k) {cast(u8 *)#k, gb_size_of(#k)-1},
ENTITY_KINDS
#undef ENTITY_KIND
};
typedef enum EntityFlag {
EntityFlag_Visited = 1<<0,
EntityFlag_Used = 1<<1,
EntityFlag_Anonymous = 1<<2,
EntityFlag_Field = 1<<3,
EntityFlag_Param = 1<<4,
EntityFlag_VectorElem = 1<<5,
} EntityFlag;
typedef struct Entity Entity;
struct Entity {
EntityKind kind;
u32 flags;
Token token;
Scope * scope;
Type * type;
AstNode * identifier; // Can be NULL
// TODO(bill): Cleanup how `using` works for entities
Entity * using_parent;
AstNode * using_expr;
union {
struct {
ExactValue value;
} Constant;
struct {
i32 field_index;
i32 field_src_index;
} Variable;
i32 TypeName;
i32 Procedure;
struct {
BuiltinProcId id;
} Builtin;
struct {
String path;
String name;
Scope *scope;
bool used;
} ImportName;
i32 Nil;
struct {
// TODO(bill): Should this be a user-level construct rather than compiler-level?
ImplicitValueId id;
Entity * backing;
} ImplicitValue;
};
};
bool is_entity_exported(Entity *e) {
if (e->kind == Entity_ImportName) {
return false;
}
// TODO(bill): Do I really want non-exported entities?
// TODO(bill): If we do, what should be the rules?
// if (e->token.string.len >= 1 &&
// e->token.string.text[0] == '_') {
// return false;
// }
return true;
}
Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *scope, Token token, Type *type) {
Entity *entity = gb_alloc_item(a, Entity);
entity->kind = kind;
entity->scope = scope;
entity->token = token;
entity->type = type;
return entity;
}
Entity *make_entity_variable(gbAllocator a, Scope *scope, Token token, Type *type) {
Entity *entity = alloc_entity(a, Entity_Variable, scope, token, type);
return entity;
}
Entity *make_entity_using_variable(gbAllocator a, Entity *parent, Token token, Type *type) {
GB_ASSERT(parent != NULL);
Entity *entity = alloc_entity(a, Entity_Variable, parent->scope, token, type);
entity->using_parent = parent;
entity->flags |= EntityFlag_Anonymous;
return entity;
}
Entity *make_entity_constant(gbAllocator a, Scope *scope, Token token, Type *type, ExactValue value) {
Entity *entity = alloc_entity(a, Entity_Constant, scope, token, type);
entity->Constant.value = value;
return entity;
}
Entity *make_entity_type_name(gbAllocator a, Scope *scope, Token token, Type *type) {
Entity *entity = alloc_entity(a, Entity_TypeName, scope, token, type);
return entity;
}
Entity *make_entity_param(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous) {
Entity *entity = make_entity_variable(a, scope, token, type);
entity->flags |= EntityFlag_Used;
entity->flags |= EntityFlag_Anonymous*(anonymous != 0);
entity->flags |= EntityFlag_Param;
return entity;
}
Entity *make_entity_field(gbAllocator a, Scope *scope, Token token, Type *type, bool anonymous, i32 field_src_index) {
Entity *entity = make_entity_variable(a, scope, token, type);
entity->Variable.field_src_index = field_src_index;
entity->Variable.field_index = field_src_index;
entity->flags |= EntityFlag_Field;
entity->flags |= EntityFlag_Anonymous*(anonymous != 0);
return entity;
}
Entity *make_entity_vector_elem(gbAllocator a, Scope *scope, Token token, Type *type, i32 field_src_index) {
Entity *entity = make_entity_variable(a, scope, token, type);
entity->Variable.field_src_index = field_src_index;
entity->Variable.field_index = field_src_index;
entity->flags |= EntityFlag_Field;
entity->flags |= EntityFlag_VectorElem;
return entity;
}
Entity *make_entity_procedure(gbAllocator a, Scope *scope, Token token, Type *signature_type) {
Entity *entity = alloc_entity(a, Entity_Procedure, scope, token, signature_type);
return entity;
}
Entity *make_entity_builtin(gbAllocator a, Scope *scope, Token token, Type *type, BuiltinProcId id) {
Entity *entity = alloc_entity(a, Entity_Builtin, scope, token, type);
entity->Builtin.id = id;
return entity;
}
Entity *make_entity_import_name(gbAllocator a, Scope *scope, Token token, Type *type,
String path, String name, Scope *import_scope) {
Entity *entity = alloc_entity(a, Entity_ImportName, scope, token, type);
entity->ImportName.path = path;
entity->ImportName.name = name;
entity->ImportName.scope = import_scope;
return entity;
}
Entity *make_entity_nil(gbAllocator a, String name, Type *type) {
Token token = make_token_ident(name);
Entity *entity = alloc_entity(a, Entity_Nil, NULL, token, type);
return entity;
}
Entity *make_entity_implicit_value(gbAllocator a, String name, Type *type, ImplicitValueId id) {
Token token = make_token_ident(name);
Entity *entity = alloc_entity(a, Entity_ImplicitValue, NULL, token, type);
entity->ImplicitValue.id = id;
return entity;
}
Entity *make_entity_dummy_variable(gbAllocator a, Scope *file_scope, Token token) {
token.string = str_lit("_");
return make_entity_variable(a, file_scope, token, NULL);
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,250 +0,0 @@
#define GB_NO_DEFER
#define GB_IMPLEMENTATION
#include "gb/gb.h"
typedef _Bool bool;
gbAllocator heap_allocator(void) {
return gb_heap_allocator();
}
#include "string.cpp"
#include "array.cpp"
gb_global String global_module_path = {0};
gb_global bool global_module_path_set = false;
String get_module_dir() {
if (global_module_path_set) {
return global_module_path;
}
Array(wchar_t) path_buf;
array_init_count(&path_buf, heap_allocator(), 300);
isize len = 0;
for (;;) {
len = GetModuleFileNameW(NULL, &path_buf.e[0], path_buf.count);
if (len == 0) {
return make_string(NULL, 0);
}
if (len < path_buf.count) {
break;
}
array_resize(&path_buf, 2*path_buf.count + 300);
}
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
GetModuleFileNameW(NULL, text, len);
String path = string16_to_string(heap_allocator(), make_string16(text, len));
for (isize i = path.len-1; i >= 0; i--) {
u8 c = path.text[i];
if (c == '/' || c == '\\') {
break;
}
path.len--;
}
global_module_path = path;
global_module_path_set = true;
gb_temp_arena_memory_end(tmp);
array_free(&path_buf);
return path;
}
String path_to_fullpath(gbAllocator a, String s) {
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
String16 string16 = string_to_string16(string_buffer_allocator, s);
String result = {0};
DWORD len = GetFullPathNameW(string16.text, 0, NULL, NULL);
if (len != 0) {
wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
GetFullPathNameW(string16.text, len, text, NULL);
text[len] = 0;
result = string16_to_string(a, make_string16(text, len));
}
gb_temp_arena_memory_end(tmp);
return result;
}
// Hasing
typedef enum HashKeyKind {
HashKey_Default,
HashKey_String,
HashKey_Pointer,
} HashKeyKind;
typedef struct HashKey {
HashKeyKind kind;
u64 key;
union {
String string; // if String, s.len > 0
void * ptr;
};
} HashKey;
gb_inline HashKey hashing_proc(void const *data, isize len) {
HashKey h = {HashKey_Default};
h.kind = HashKey_Default;
// h.key = gb_murmur64(data, len);
h.key = gb_fnv64a(data, len);
return h;
}
gb_inline HashKey hash_string(String s) {
HashKey h = hashing_proc(s.text, s.len);
h.kind = HashKey_String;
h.string = s;
return h;
}
gb_inline HashKey hash_pointer(void *ptr) {
HashKey h = {HashKey_Default};
h.key = cast(u64)cast(uintptr)ptr;
h.ptr = ptr;
h.kind = HashKey_Default;
return h;
}
bool hash_key_equal(HashKey a, HashKey b) {
if (a.key == b.key) {
// NOTE(bill): If two string's hashes collide, compare the strings themselves
if (a.kind == HashKey_String) {
if (b.kind == HashKey_String) {
return str_eq(a.string, b.string);
}
return false;
}
return true;
}
return false;
}
i64 next_pow2(i64 n) {
if (n <= 0) {
return 0;
}
n--;
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n |= n >> 32;
n++;
return n;
}
i64 prev_pow2(i64 n) {
if (n <= 0) {
return 0;
}
n |= n >> 1;
n |= n >> 2;
n |= n >> 4;
n |= n >> 8;
n |= n >> 16;
n |= n >> 32;
return n - (n >> 1);
}
i16 f32_to_f16(f32 value) {
union { u32 i; f32 f; } v;
i32 i, s, e, m;
v.f = value;
i = (i32)v.i;
s = (i >> 16) & 0x00008000;
e = ((i >> 23) & 0x000000ff) - (127 - 15);
m = i & 0x007fffff;
if (e <= 0) {
if (e < -10) return cast(i16)s;
m = (m | 0x00800000) >> (1 - e);
if (m & 0x00001000)
m += 0x00002000;
return cast(i16)(s | (m >> 13));
} else if (e == 0xff - (127 - 15)) {
if (m == 0) {
return cast(i16)(s | 0x7c00); /* NOTE(bill): infinity */
} else {
/* NOTE(bill): NAN */
m >>= 13;
return cast(i16)(s | 0x7c00 | m | (m == 0));
}
} else {
if (m & 0x00001000) {
m += 0x00002000;
if (m & 0x00800000) {
m = 0;
e += 1;
}
}
if (e > 30) {
float volatile f = 1e12f;
int j;
for (j = 0; j < 10; j++)
f *= f; /* NOTE(bill): Cause overflow */
return cast(i16)(s | 0x7c00);
}
return cast(i16)(s | (e << 10) | (m >> 13));
}
}
#define for_array(index_, array_) for (isize index_ = 0; index_ < (array_).count; index_++)
// Doubly Linked Lists
#define DLIST_SET(curr_element, next_element) do { \
(curr_element)->next = (next_element); \
(curr_element)->next->prev = (curr_element); \
(curr_element) = (curr_element)->next; \
} while (0)
#define DLIST_APPEND(root_element, curr_element, next_element) do { \
if ((root_element) == NULL) { \
(root_element) = (curr_element) = (next_element); \
} else { \
DLIST_SET(curr_element, next_element); \
} \
} while (0)
////////////////////////////////////////////////////////////////
//
// Generic Data Structures
//
////////////////////////////////////////////////////////////////
#define MAP_TYPE String
#define MAP_FUNC map_string_
#define MAP_NAME MapString
#include "map.c"
#define MAP_TYPE bool
#define MAP_FUNC map_bool_
#define MAP_NAME MapBool
#include "map.c"
#define MAP_TYPE isize
#define MAP_FUNC map_isize_
#define MAP_NAME MapIsize
#include "map.c"

View File

@@ -1,400 +0,0 @@
#include <math.h>
// TODO(bill): Big numbers
// IMPORTANT TODO(bill): This needs to be completely fixed!!!!!!!!
typedef struct AstNode AstNode;
typedef enum ExactValueKind {
ExactValue_Invalid,
ExactValue_Bool,
ExactValue_String,
ExactValue_Integer,
ExactValue_Float,
ExactValue_Pointer,
ExactValue_Compound, // TODO(bill): Is this good enough?
ExactValue_Count,
} ExactValueKind;
typedef struct ExactValue {
ExactValueKind kind;
union {
bool value_bool;
String value_string;
i64 value_integer; // NOTE(bill): This must be an integer and not a pointer
f64 value_float;
i64 value_pointer;
AstNode *value_compound;
};
} ExactValue;
HashKey hash_exact_value(ExactValue v) {
return hashing_proc(&v, gb_size_of(ExactValue));
}
ExactValue make_exact_value_compound(AstNode *node) {
ExactValue result = {ExactValue_Compound};
result.value_compound = node;
return result;
}
ExactValue make_exact_value_bool(bool b) {
ExactValue result = {ExactValue_Bool};
result.value_bool = (b != 0);
return result;
}
ExactValue make_exact_value_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
ExactValue result = {ExactValue_String};
result.value_string = string;
return result;
}
ExactValue make_exact_value_integer_from_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
ExactValue result = {ExactValue_Integer};
i32 base = 10;
if (string.text[0] == '0') {
switch (string.text[1]) {
case 'b': base = 2; break;
case 'o': base = 8; break;
case 'd': base = 10; break;
case 'x': base = 16; break;
}
}
result.value_integer = gb_str_to_i64(cast(char *)string.text, NULL, base);
return result;
}
ExactValue make_exact_value_integer(i64 i) {
ExactValue result = {ExactValue_Integer};
result.value_integer = i;
return result;
}
ExactValue make_exact_value_float_from_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
ExactValue result = {ExactValue_Float};
result.value_float = gb_str_to_f64(cast(char *)string.text, NULL);
return result;
}
ExactValue make_exact_value_float(f64 f) {
ExactValue result = {ExactValue_Float};
result.value_float = f;
return result;
}
ExactValue make_exact_value_pointer(i64 ptr) {
ExactValue result = {ExactValue_Pointer};
result.value_pointer = ptr;
return result;
}
ExactValue make_exact_value_from_basic_literal(Token token) {
switch (token.kind) {
case Token_String: return make_exact_value_string(token.string);
case Token_Integer: return make_exact_value_integer_from_string(token.string);
case Token_Float: return make_exact_value_float_from_string(token.string);
case Token_Rune: {
Rune r = GB_RUNE_INVALID;
gb_utf8_decode(token.string.text, token.string.len, &r);
// gb_printf("%.*s rune: %d\n", LIT(token.string), r);
return make_exact_value_integer(r);
}
default:
GB_PANIC("Invalid token for basic literal");
break;
}
ExactValue result = {ExactValue_Invalid};
return result;
}
ExactValue exact_value_to_integer(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return v;
case ExactValue_Float: {
i64 i = cast(i64)v.value_float;
f64 f = cast(f64)i;
if (f == v.value_float) {
return make_exact_value_integer(i);
}
} break;
case ExactValue_Pointer:
return make_exact_value_integer(cast(i64)cast(intptr)v.value_pointer);
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_value_to_float(ExactValue v) {
switch (v.kind) {
case ExactValue_Integer:
return make_exact_value_float(cast(i64)v.value_integer);
case ExactValue_Float:
return v;
}
ExactValue r = {ExactValue_Invalid};
return r;
}
ExactValue exact_unary_operator_value(Token op, ExactValue v, i32 precision) {
switch (op.kind) {
case Token_Add: {
switch (v.kind) {
case ExactValue_Invalid:
case ExactValue_Integer:
case ExactValue_Float:
return v;
}
} break;
case Token_Sub: {
switch (v.kind) {
case ExactValue_Invalid:
return v;
case ExactValue_Integer: {
ExactValue i = v;
i.value_integer = -i.value_integer;
return i;
}
case ExactValue_Float: {
ExactValue i = v;
i.value_float = -i.value_float;
return i;
}
}
} break;
case Token_Xor: {
i64 i = 0;
switch (v.kind) {
case ExactValue_Invalid:
return v;
case ExactValue_Integer:
i = v.value_integer;
i = ~i;
break;
default:
goto failure;
}
// NOTE(bill): unsigned integers will be negative and will need to be
// limited to the types precision
if (precision > 0)
i &= ~((~0ll)<<precision);
return make_exact_value_integer(i);
} break;
case Token_Not: {
switch (v.kind) {
case ExactValue_Invalid: return v;
case ExactValue_Bool:
return make_exact_value_bool(!v.value_bool);
}
} break;
}
failure:
GB_PANIC("Invalid unary operation, %.*s", LIT(token_strings[op.kind]));
ExactValue error_value = {0};
return error_value;
}
// NOTE(bill): Make sure things are evaluated in correct order
i32 exact_value_order(ExactValue v) {
switch (v.kind) {
case ExactValue_Invalid:
return 0;
case ExactValue_Bool:
case ExactValue_String:
return 1;
case ExactValue_Integer:
return 2;
case ExactValue_Float:
return 3;
case ExactValue_Pointer:
return 4;
default:
GB_PANIC("How'd you get here? Invalid Value.kind");
return -1;
}
}
void match_exact_values(ExactValue *x, ExactValue *y) {
if (exact_value_order(*y) < exact_value_order(*x)) {
match_exact_values(y, x);
return;
}
switch (x->kind) {
case ExactValue_Invalid:
*y = *x;
return;
case ExactValue_Bool:
case ExactValue_String:
return;
case ExactValue_Integer:
switch (y->kind) {
case ExactValue_Integer:
return;
case ExactValue_Float:
// TODO(bill): Is this good enough?
*x = make_exact_value_float(cast(f64)x->value_integer);
return;
}
break;
case ExactValue_Float:
if (y->kind == ExactValue_Float)
return;
break;
}
compiler_error("How'd you get here? Invalid ExactValueKind");
}
// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
ExactValue exact_binary_operator_value(Token op, ExactValue x, ExactValue y) {
match_exact_values(&x, &y);
switch (x.kind) {
case ExactValue_Invalid:
return x;
case ExactValue_Bool:
switch (op.kind) {
case Token_CmpAnd: return make_exact_value_bool(x.value_bool && y.value_bool);
case Token_CmpOr: return make_exact_value_bool(x.value_bool || y.value_bool);
case Token_And: return make_exact_value_bool(x.value_bool & y.value_bool);
case Token_Or: return make_exact_value_bool(x.value_bool | y.value_bool);
default: goto error;
}
break;
case ExactValue_Integer: {
i64 a = x.value_integer;
i64 b = y.value_integer;
i64 c = 0;
switch (op.kind) {
case Token_Add: c = a + b; break;
case Token_Sub: c = a - b; break;
case Token_Mul: c = a * b; break;
case Token_Quo: return make_exact_value_float(fmod(cast(f64)a, cast(f64)b));
case Token_QuoEq: c = a / b; break; // NOTE(bill): Integer division
case Token_Mod: c = a % b; break;
case Token_And: c = a & b; break;
case Token_Or: c = a | b; break;
case Token_Xor: c = a ^ b; break;
case Token_AndNot: c = a&(~b); break;
case Token_Shl: c = a << b; break;
case Token_Shr: c = a >> b; break;
default: goto error;
}
return make_exact_value_integer(c);
} break;
case ExactValue_Float: {
f64 a = x.value_float;
f64 b = y.value_float;
switch (op.kind) {
case Token_Add: return make_exact_value_float(a + b);
case Token_Sub: return make_exact_value_float(a - b);
case Token_Mul: return make_exact_value_float(a * b);
case Token_Quo: return make_exact_value_float(a / b);
default: goto error;
}
} break;
}
error:
ExactValue error_value = {0};
// gb_printf_err("Invalid binary operation: %s\n", token_kind_to_string(op.kind));
return error_value;
}
gb_inline ExactValue exact_value_add(ExactValue x, ExactValue y) { Token op = {Token_Add}; return exact_binary_operator_value(op, x, y); }
gb_inline ExactValue exact_value_sub(ExactValue x, ExactValue y) { Token op = {Token_Sub}; return exact_binary_operator_value(op, x, y); }
gb_inline ExactValue exact_value_mul(ExactValue x, ExactValue y) { Token op = {Token_Mul}; return exact_binary_operator_value(op, x, y); }
gb_inline ExactValue exact_value_quo(ExactValue x, ExactValue y) { Token op = {Token_Quo}; return exact_binary_operator_value(op, x, y); }
gb_inline ExactValue exact_value_shift(Token op, ExactValue x, ExactValue y) { return exact_binary_operator_value(op, x, y); }
i32 cmp_f64(f64 a, f64 b) {
return (a > b) - (a < b);
}
bool compare_exact_values(Token op, ExactValue x, ExactValue y) {
match_exact_values(&x, &y);
switch (x.kind) {
case ExactValue_Invalid:
return false;
case ExactValue_Bool:
switch (op.kind) {
case Token_CmpEq: return x.value_bool == y.value_bool;
case Token_NotEq: return x.value_bool != y.value_bool;
}
break;
case ExactValue_Integer: {
i64 a = x.value_integer;
i64 b = y.value_integer;
switch (op.kind) {
case Token_CmpEq: return a == b;
case Token_NotEq: return a != b;
case Token_Lt: return a < b;
case Token_LtEq: return a <= b;
case Token_Gt: return a > b;
case Token_GtEq: return a >= b;
}
} break;
case ExactValue_Float: {
f64 a = x.value_float;
f64 b = y.value_float;
switch (op.kind) {
case Token_CmpEq: return cmp_f64(a, b) == 0;
case Token_NotEq: return cmp_f64(a, b) != 0;
case Token_Lt: return cmp_f64(a, b) < 0;
case Token_LtEq: return cmp_f64(a, b) <= 0;
case Token_Gt: return cmp_f64(a, b) > 0;
case Token_GtEq: return cmp_f64(a, b) >= 0;
}
} break;
case ExactValue_String: {
String a = x.value_string;
String b = y.value_string;
isize len = gb_min(a.len, b.len);
// TODO(bill): gb_memcompare is used because the strings are UTF-8
switch (op.kind) {
case Token_CmpEq: return gb_memcompare(a.text, b.text, len) == 0;
case Token_NotEq: return gb_memcompare(a.text, b.text, len) != 0;
case Token_Lt: return gb_memcompare(a.text, b.text, len) < 0;
case Token_LtEq: return gb_memcompare(a.text, b.text, len) <= 0;
case Token_Gt: return gb_memcompare(a.text, b.text, len) > 0;
case Token_GtEq: return gb_memcompare(a.text, b.text, len) >= 0;
}
} break;
}
GB_PANIC("Invalid comparison");
return false;
}

View File

@@ -1,265 +0,0 @@
#define VERSION_STRING "v0.0.3"
#include "common.cpp"
#include "timings.cpp"
#include "unicode.cpp"
#include "tokenizer.cpp"
#include "parser.cpp"
// #include "printer.cpp"
#include "checker/checker.cpp"
#include "ssa.cpp"
#include "ssa_opt.cpp"
#include "ssa_print.cpp"
// #include "vm.cpp"
// NOTE(bill): `name` is used in debugging and profiling modes
i32 win32_exec_command_line_app(char *name, char *fmt, ...) {
STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
PROCESS_INFORMATION pi = {0};
char cmd_line[4096] = {0};
isize cmd_len;
va_list va;
gbTempArenaMemory tmp;
String16 cmd;
i32 exit_code = 0;
start_info.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
start_info.wShowWindow = SW_SHOW;
start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
start_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
va_start(va, fmt);
cmd_len = gb_snprintf_va(cmd_line, gb_size_of(cmd_line), fmt, va);
va_end(va);
// gb_printf("%.*s\n", cast(int)cmd_len, cmd_line);
tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
cmd = string_to_string16(string_buffer_allocator, make_string(cast(u8 *)cmd_line, cmd_len-1));
if (CreateProcessW(NULL, cmd.text,
NULL, NULL, true, 0, NULL, NULL,
&start_info, &pi)) {
WaitForSingleObject(pi.hProcess, INFINITE);
GetExitCodeProcess(pi.hProcess, cast(DWORD *)&exit_code);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
} else {
// NOTE(bill): failed to create process
gb_printf_err("Failed to execute command:\n\t%s\n", cmd_line);
exit_code = -1;
}
gb_temp_arena_memory_end(tmp);
return exit_code;
}
typedef enum ArchKind {
ArchKind_x64,
ArchKind_x86,
} ArchKind;
typedef struct ArchData {
BaseTypeSizes sizes;
String llc_flags;
String link_flags;
} ArchData;
ArchData make_arch_data(ArchKind kind) {
ArchData data = {0};
switch (kind) {
case ArchKind_x64:
default:
data.sizes.word_size = 8;
data.sizes.max_align = 16;
data.llc_flags = str_lit("-march=x86-64 ");
data.link_flags = str_lit("/machine:x64 ");
break;
case ArchKind_x86:
data.sizes.word_size = 4;
data.sizes.max_align = 8;
data.llc_flags = str_lit("-march=x86 ");
data.link_flags = str_lit("/machine:x86 ");
break;
}
return data;
}
void usage(char *argv0) {
gb_printf_err("%s is a tool for managing Odin source code\n", argv0);
gb_printf_err("Usage:");
gb_printf_err("\n\t%s command [arguments]\n", argv0);
gb_printf_err("Commands:");
gb_printf_err("\n\tbuild compile .odin file");
gb_printf_err("\n\trun compile and run .odin file");
gb_printf_err("\n\tversion print Odin version");
gb_printf_err("\n\n");
}
int main(int argc, char **argv) {
if (argc < 2) {
usage(argv[0]);
return 1;
}
Timings timings = {0};
timings_init(&timings, str_lit("Total Time"), 128);
// defer (timings_destroy(&timings));
#if 1
init_string_buffer_memory();
init_global_error_collector();
String module_dir = get_module_dir();
init_universal_scope();
char *init_filename = NULL;
bool run_output = false;
String arg1 = make_string_c(argv[1]);
if (str_eq(arg1, str_lit("run"))) {
run_output = true;
init_filename = argv[2];
} else if (str_eq(arg1, str_lit("build"))) {
init_filename = argv[2];
} else if (str_eq(arg1, str_lit("version"))) {
gb_printf("%s version %s", argv[0], VERSION_STRING);
return 0;
} else {
usage(argv[0]);
return 1;
}
// TODO(bill): prevent compiling without a linker
timings_start_section(&timings, str_lit("parse files"));
Parser parser = {0};
if (!init_parser(&parser)) {
return 1;
}
// defer (destroy_parser(&parser));
if (parse_files(&parser, init_filename) != ParseFile_None) {
return 1;
}
#if 1
timings_start_section(&timings, str_lit("type check"));
Checker checker = {0};
ArchData arch_data = make_arch_data(ArchKind_x64);
init_checker(&checker, &parser, arch_data.sizes);
// defer (destroy_checker(&checker));
check_parsed_files(&checker);
#endif
#if 1
ssaGen ssa = {0};
if (!ssa_gen_init(&ssa, &checker)) {
return 1;
}
// defer (ssa_gen_destroy(&ssa));
timings_start_section(&timings, str_lit("ssa gen"));
ssa_gen_tree(&ssa);
timings_start_section(&timings, str_lit("ssa opt"));
ssa_opt_tree(&ssa);
timings_start_section(&timings, str_lit("ssa print"));
ssa_print_llvm_ir(&ssa);
// prof_print_all();
#if 1
timings_start_section(&timings, str_lit("llvm-opt"));
char const *output_name = ssa.output_file.filename;
isize base_name_len = gb_path_extension(output_name)-1 - output_name;
String output = make_string(cast(u8 *)output_name, base_name_len);
i32 optimization_level = 0;
optimization_level = gb_clamp(optimization_level, 0, 3);
i32 exit_code = 0;
// For more passes arguments: http://llvm.org/docs/Passes.html
exit_code = win32_exec_command_line_app("llvm-opt",
"%.*sbin/opt %s -o %.*s.bc "
"-mem2reg "
"-memcpyopt "
"-die "
// "-dse "
// "-dce "
// "-S "
"",
LIT(module_dir),
output_name, LIT(output));
if (exit_code != 0) {
return exit_code;
}
#if 1
timings_start_section(&timings, str_lit("llvm-llc"));
// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
exit_code = win32_exec_command_line_app("llvm-llc",
"%.*sbin/llc %.*s.bc -filetype=obj -O%d "
"%.*s "
// "-debug-pass=Arguments "
"",
LIT(module_dir),
LIT(output),
optimization_level,
LIT(arch_data.llc_flags));
if (exit_code != 0) {
return exit_code;
}
timings_start_section(&timings, str_lit("msvc-link"));
gbString lib_str = gb_string_make(heap_allocator(), "Kernel32.lib");
// defer (gb_string_free(lib_str));
char lib_str_buf[1024] = {0};
for_array(i, parser.foreign_libraries) {
String lib = parser.foreign_libraries.e[i];
isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
" %.*s.lib", LIT(lib));
lib_str = gb_string_appendc(lib_str, lib_str_buf);
}
exit_code = win32_exec_command_line_app("msvc-link",
"link %.*s.obj -OUT:%.*s.exe %s "
"/defaultlib:libcmt "
"/nologo /incremental:no /opt:ref /subsystem:console "
" %.*s "
"",
LIT(output), LIT(output),
lib_str, LIT(arch_data.link_flags));
if (exit_code != 0) {
return exit_code;
}
// timings_print_all(&timings);
if (run_output) {
win32_exec_command_line_app("odin run",
"%.*s.exe", cast(int)base_name_len, output_name);
}
#endif
#endif
#endif
#endif
return 0;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,221 +0,0 @@
gb_inline void print_indent(isize indent) {
while (indent --> 0)
gb_printf(" ");
}
void print_ast(AstNode *node, isize indent) {
if (node == NULL)
return;
switch (node->kind) {
case AstNode_BasicLit:
print_indent(indent);
print_token(node->BasicLit);
break;
case AstNode_Ident:
print_indent(indent);
print_token(node->Ident);
break;
case AstNode_ProcLit:
print_indent(indent);
gb_printf("(proc lit)\n");
print_ast(node->ProcLit.type, indent+1);
print_ast(node->ProcLit.body, indent+1);
break;
case AstNode_CompoundLit:
print_indent(indent);
gb_printf("(compound lit)\n");
print_ast(node->CompoundLit.type, indent+1);
for_array(i, node->CompoundLit.elems) {
print_ast(node->CompoundLit.elems[i], indent+1);
}
break;
case AstNode_TagExpr:
print_indent(indent);
gb_printf("(tag)\n");
print_indent(indent+1);
print_token(node->TagExpr.name);
print_ast(node->TagExpr.expr, indent+1);
break;
case AstNode_UnaryExpr:
print_indent(indent);
print_token(node->UnaryExpr.op);
print_ast(node->UnaryExpr.expr, indent+1);
break;
case AstNode_BinaryExpr:
print_indent(indent);
print_token(node->BinaryExpr.op);
print_ast(node->BinaryExpr.left, indent+1);
print_ast(node->BinaryExpr.right, indent+1);
break;
case AstNode_CallExpr:
print_indent(indent);
gb_printf("(call)\n");
print_ast(node->CallExpr.proc, indent+1);
for_array(i, node->CallExpr.args) {
print_ast(node->CallExpr.args[i], indent+1);
}
break;
case AstNode_SelectorExpr:
print_indent(indent);
gb_printf(".\n");
print_ast(node->SelectorExpr.expr, indent+1);
print_ast(node->SelectorExpr.selector, indent+1);
break;
case AstNode_IndexExpr:
print_indent(indent);
gb_printf("([])\n");
print_ast(node->IndexExpr.expr, indent+1);
print_ast(node->IndexExpr.index, indent+1);
break;
case AstNode_DerefExpr:
print_indent(indent);
gb_printf("(deref)\n");
print_ast(node->DerefExpr.expr, indent+1);
break;
case AstNode_ExprStmt:
print_ast(node->ExprStmt.expr, indent);
break;
case AstNode_IncDecStmt:
print_indent(indent);
print_token(node->IncDecStmt.op);
print_ast(node->IncDecStmt.expr, indent+1);
break;
case AstNode_AssignStmt:
print_indent(indent);
print_token(node->AssignStmt.op);
for_array(i, node->AssignStmt.lhs) {
print_ast(node->AssignStmt.lhs[i], indent+1);
}
for_array(i, node->AssignStmt.rhs) {
print_ast(node->AssignStmt.rhs[i], indent+1);
}
break;
case AstNode_BlockStmt:
print_indent(indent);
gb_printf("(block)\n");
for_array(i, node->BlockStmt.stmts) {
print_ast(node->BlockStmt.stmts[i], indent+1);
}
break;
case AstNode_IfStmt:
print_indent(indent);
gb_printf("(if)\n");
print_ast(node->IfStmt.cond, indent+1);
print_ast(node->IfStmt.body, indent+1);
if (node->IfStmt.else_stmt) {
print_indent(indent);
gb_printf("(else)\n");
print_ast(node->IfStmt.else_stmt, indent+1);
}
break;
case AstNode_ReturnStmt:
print_indent(indent);
gb_printf("(return)\n");
for_array(i, node->ReturnStmt.results) {
print_ast(node->ReturnStmt.results[i], indent+1);
}
break;
case AstNode_ForStmt:
print_indent(indent);
gb_printf("(for)\n");
print_ast(node->ForStmt.init, indent+1);
print_ast(node->ForStmt.cond, indent+1);
print_ast(node->ForStmt.post, indent+1);
print_ast(node->ForStmt.body, indent+1);
break;
case AstNode_DeferStmt:
print_indent(indent);
gb_printf("(defer)\n");
print_ast(node->DeferStmt.stmt, indent+1);
break;
case AstNode_VarDecl:
print_indent(indent);
gb_printf("(decl:var)\n");
for_array(i, node->VarDecl.names) {
print_ast(node->VarDecl.names[i], indent+1);
}
print_ast(node->VarDecl.type, indent+1);
for_array(i, node->VarDecl.values) {
print_ast(node->VarDecl.values[i], indent+1);
}
break;
case AstNode_ConstDecl:
print_indent(indent);
gb_printf("(decl:const)\n");
for_array(i, node->VarDecl.names) {
print_ast(node->VarDecl.names[i], indent+1);
}
print_ast(node->VarDecl.type, indent+1);
for_array(i, node->VarDecl.values) {
print_ast(node->VarDecl.values[i], indent+1);
}
break;
case AstNode_ProcDecl:
print_indent(indent);
gb_printf("(decl:proc)\n");
print_ast(node->ProcDecl.type, indent+1);
print_ast(node->ProcDecl.body, indent+1);
break;
case AstNode_TypeDecl:
print_indent(indent);
gb_printf("(type)\n");
print_ast(node->TypeDecl.name, indent+1);
print_ast(node->TypeDecl.type, indent+1);
break;
case AstNode_ProcType:
print_indent(indent);
gb_printf("(type:proc)(%td -> %td)\n", node->ProcType.params.count, node->ProcType.results.count);
for_array(i, node->ProcType.params) {
print_ast(node->ProcType.params[i], indent+1);
}
if (node->ProcType.results.count > 0) {
print_indent(indent+1);
gb_printf("->\n");
for_array(i, node->ProcType.results) {
print_ast(node->ProcType.results[i], indent+1);
}
}
break;
case AstNode_Parameter:
for_array(i, node->Parameter.names) {
print_ast(node->Parameter.names[i], indent+1);
}
print_ast(node->Parameter.type, indent);
break;
case AstNode_PointerType:
print_indent(indent);
print_token(node->PointerType.token);
print_ast(node->PointerType.type, indent+1);
break;
case AstNode_ArrayType:
print_indent(indent);
gb_printf("[]\n");
print_ast(node->ArrayType.count, indent+1);
print_ast(node->ArrayType.elem, indent+1);
break;
case AstNode_StructType:
print_indent(indent);
gb_printf("(struct)\n");
for_array(i, node->StructType.decls) {
print_ast(node->StructType.decls[i], indent+1);
}
break;
}
// if (node->next)
// print_ast(node->next, indent);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,493 +0,0 @@
// Optimizations for the SSA code
void ssa_opt_add_operands(ssaValueArray *ops, ssaInstr *i) {
switch (i->kind) {
case ssaInstr_Comment:
break;
case ssaInstr_Local:
break;
case ssaInstr_ZeroInit:
array_add(ops, i->ZeroInit.address);
break;
case ssaInstr_Store:
array_add(ops, i->Store.address);
array_add(ops, i->Store.value);
break;
case ssaInstr_Load:
array_add(ops, i->Load.address);
break;
case ssaInstr_ArrayElementPtr:
array_add(ops, i->ArrayElementPtr.address);
array_add(ops, i->ArrayElementPtr.elem_index);
break;
case ssaInstr_StructElementPtr:
array_add(ops, i->StructElementPtr.address);
break;
case ssaInstr_PtrOffset:
array_add(ops, i->PtrOffset.address);
array_add(ops, i->PtrOffset.offset);
break;
case ssaInstr_ArrayExtractValue:
array_add(ops, i->ArrayExtractValue.address);
break;
case ssaInstr_StructExtractValue:
array_add(ops, i->StructExtractValue.address);
break;
case ssaInstr_Conv:
array_add(ops, i->Conv.value);
break;
case ssaInstr_Jump:
break;
case ssaInstr_If:
array_add(ops, i->If.cond);
break;
case ssaInstr_Return:
if (i->Return.value != NULL) {
array_add(ops, i->Return.value);
}
break;
case ssaInstr_Select:
array_add(ops, i->Select.cond);
break;
case ssaInstr_Phi:
for_array(j, i->Phi.edges) {
array_add(ops, i->Phi.edges.e[j]);
}
break;
case ssaInstr_Unreachable: break;
case ssaInstr_BinaryOp:
array_add(ops, i->BinaryOp.left);
array_add(ops, i->BinaryOp.right);
break;
case ssaInstr_Call:
array_add(ops, i->Call.value);
for (isize j = 0; j < i->Call.arg_count; j++) {
array_add(ops, i->Call.args[j]);
}
break;
case ssaInstr_VectorExtractElement:
array_add(ops, i->VectorExtractElement.vector);
array_add(ops, i->VectorExtractElement.index);
break;
case ssaInstr_VectorInsertElement:
array_add(ops, i->VectorInsertElement.vector);
array_add(ops, i->VectorInsertElement.elem);
array_add(ops, i->VectorInsertElement.index);
break;
case ssaInstr_VectorShuffle:
array_add(ops, i->VectorShuffle.vector);
break;
case ssaInstr_StartupRuntime:
break;
case ssaInstr_BoundsCheck:
array_add(ops, i->BoundsCheck.index);
array_add(ops, i->BoundsCheck.len);
break;
case ssaInstr_SliceBoundsCheck:
array_add(ops, i->SliceBoundsCheck.low);
array_add(ops, i->SliceBoundsCheck.high);
array_add(ops, i->SliceBoundsCheck.max);
break;
}
}
void ssa_opt_block_replace_pred(ssaBlock *b, ssaBlock *from, ssaBlock *to) {
for_array(i, b->preds) {
ssaBlock *pred = b->preds.e[i];
if (pred == from) {
b->preds.e[i] = to;
}
}
}
void ssa_opt_block_replace_succ(ssaBlock *b, ssaBlock *from, ssaBlock *to) {
for_array(i, b->succs) {
ssaBlock *succ = b->succs.e[i];
if (succ == from) {
b->succs.e[i] = to;
}
}
}
bool ssa_opt_block_has_phi(ssaBlock *b) {
return b->instrs.e[0]->Instr.kind == ssaInstr_Phi;
}
ssaValueArray ssa_get_block_phi_nodes(ssaBlock *b) {
ssaValueArray phis = {0};
for_array(i, b->instrs) {
ssaInstr *instr = &b->instrs.e[i]->Instr;
if (instr->kind != ssaInstr_Phi) {
phis = b->instrs;
phis.count = i;
return phis;
}
}
return phis;
}
void ssa_remove_pred(ssaBlock *b, ssaBlock *p) {
ssaValueArray phis = ssa_get_block_phi_nodes(b);
isize i = 0;
for_array(j, b->preds) {
ssaBlock *pred = b->preds.e[j];
if (pred != p) {
b->preds.e[i] = b->preds.e[j];
for_array(k, phis) {
ssaInstrPhi *phi = &phis.e[k]->Instr.Phi;
phi->edges.e[i] = phi->edges.e[j];
}
i++;
}
}
b->preds.count = i;
for_array(k, phis) {
ssaInstrPhi *phi = &phis.e[k]->Instr.Phi;
phi->edges.count = i;
}
}
void ssa_remove_dead_blocks(ssaProcedure *proc) {
isize j = 0;
for_array(i, proc->blocks) {
ssaBlock *b = proc->blocks.e[i];
if (b == NULL) {
continue;
}
// NOTE(bill): Swap order
b->index = j;
proc->blocks.e[j++] = b;
}
proc->blocks.count = j;
}
void ssa_mark_reachable(ssaBlock *b) {
isize const WHITE = 0;
isize const BLACK = -1;
b->index = BLACK;
for_array(i, b->succs) {
ssaBlock *succ = b->succs.e[i];
if (succ->index == WHITE) {
ssa_mark_reachable(succ);
}
}
}
void ssa_remove_unreachable_blocks(ssaProcedure *proc) {
isize const WHITE = 0;
isize const BLACK = -1;
for_array(i, proc->blocks) {
proc->blocks.e[i]->index = WHITE;
}
ssa_mark_reachable(proc->blocks.e[0]);
for_array(i, proc->blocks) {
ssaBlock *b = proc->blocks.e[i];
if (b->index == WHITE) {
for_array(j, b->succs) {
ssaBlock *c = b->succs.e[j];
if (c->index == BLACK) {
ssa_remove_pred(c, b);
}
}
// NOTE(bill): Mark as empty but don't actually free it
// As it's been allocated with an arena
proc->blocks.e[i] = NULL;
}
}
ssa_remove_dead_blocks(proc);
}
bool ssa_opt_block_fusion(ssaProcedure *proc, ssaBlock *a) {
if (a->succs.count != 1) {
return false;
}
ssaBlock *b = a->succs.e[0];
if (b->preds.count != 1) {
return false;
}
if (ssa_opt_block_has_phi(b)) {
return false;
}
array_pop(&a->instrs); // Remove branch at end
for_array(i, b->instrs) {
array_add(&a->instrs, b->instrs.e[i]);
ssa_set_instr_parent(b->instrs.e[i], a);
}
array_clear(&a->succs);
for_array(i, b->succs) {
array_add(&a->succs, b->succs.e[i]);
}
// Fix preds links
for_array(i, b->succs) {
ssa_opt_block_replace_pred(b->succs.e[i], b, a);
}
proc->blocks.e[b->index] = NULL;
return true;
}
void ssa_opt_blocks(ssaProcedure *proc) {
ssa_remove_unreachable_blocks(proc);
#if 1
bool changed = true;
while (changed) {
changed = false;
for_array(i, proc->blocks) {
ssaBlock *b = proc->blocks.e[i];
if (b == NULL) {
continue;
}
GB_ASSERT(b->index == i);
if (ssa_opt_block_fusion(proc, b)) {
changed = true;
}
// TODO(bill): other simple block optimizations
}
}
#endif
ssa_remove_dead_blocks(proc);
}
void ssa_opt_build_referrers(ssaProcedure *proc) {
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
ssaValueArray ops = {0}; // NOTE(bill): Act as a buffer
array_init_reserve(&ops, proc->module->tmp_allocator, 64); // HACK(bill): This _could_ overflow the temp arena
for_array(i, proc->blocks) {
ssaBlock *b = proc->blocks.e[i];
for_array(j, b->instrs) {
ssaValue *instr = b->instrs.e[j];
array_clear(&ops);
ssa_opt_add_operands(&ops, &instr->Instr);
for_array(k, ops) {
ssaValue *op = ops.e[k];
if (op == NULL) {
continue;
}
ssaValueArray *refs = ssa_value_referrers(op);
if (refs != NULL) {
array_add(refs, instr);
}
}
}
}
gb_temp_arena_memory_end(tmp);
}
// State of Lengauer-Tarjan algorithm
// Based on this paper: http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf
typedef struct ssaLTState {
isize count;
// NOTE(bill): These are arrays
ssaBlock **sdom; // Semidominator
ssaBlock **parent; // Parent in DFS traversal of CFG
ssaBlock **ancestor;
} ssaLTState;
// §2.2 - bottom of page
void ssa_lt_link(ssaLTState *lt, ssaBlock *p, ssaBlock *q) {
lt->ancestor[q->index] = p;
}
i32 ssa_lt_depth_first_search(ssaLTState *lt, ssaBlock *p, i32 i, ssaBlock **preorder) {
preorder[i] = p;
p->dom.pre = i++;
lt->sdom[p->index] = p;
ssa_lt_link(lt, NULL, p);
for_array(index, p->succs) {
ssaBlock *q = p->succs.e[index];
if (lt->sdom[q->index] == NULL) {
lt->parent[q->index] = p;
i = ssa_lt_depth_first_search(lt, q, i, preorder);
}
}
return i;
}
ssaBlock *ssa_lt_eval(ssaLTState *lt, ssaBlock *v) {
ssaBlock *u = v;
for (;
lt->ancestor[v->index] != NULL;
v = lt->ancestor[v->index]) {
if (lt->sdom[v->index]->dom.pre < lt->sdom[u->index]->dom.pre) {
u = v;
}
}
return u;
}
typedef struct ssaDomPrePost {
i32 pre, post;
} ssaDomPrePost;
ssaDomPrePost ssa_opt_number_dom_tree(ssaBlock *v, i32 pre, i32 post) {
ssaDomPrePost result = {pre, post};
v->dom.pre = pre++;
for_array(i, v->dom.children) {
result = ssa_opt_number_dom_tree(v->dom.children.e[i], result.pre, result.post);
}
v->dom.post = post++;
result.pre = pre;
result.post = post;
return result;
}
// NOTE(bill): Requires `ssa_opt_blocks` to be called before this
void ssa_opt_build_dom_tree(ssaProcedure *proc) {
// Based on this paper: http://jgaa.info/accepted/2006/GeorgiadisTarjanWerneck2006.10.1.pdf
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&proc->module->tmp_arena);
isize n = proc->blocks.count;
ssaBlock **buf = gb_alloc_array(proc->module->tmp_allocator, ssaBlock *, 5*n);
ssaLTState lt = {0};
lt.count = n;
lt.sdom = &buf[0*n];
lt.parent = &buf[1*n];
lt.ancestor = &buf[2*n];
ssaBlock **preorder = &buf[3*n];
ssaBlock **buckets = &buf[4*n];
ssaBlock *root = proc->blocks.e[0];
// Step 1 - number vertices
i32 pre_num = ssa_lt_depth_first_search(&lt, root, 0, preorder);
gb_memmove(buckets, preorder, n*gb_size_of(preorder[0]));
for (i32 i = n-1; i > 0; i--) {
ssaBlock *w = preorder[i];
// Step 3 - Implicitly define idom for nodes
for (ssaBlock *v = buckets[i]; v != w; v = buckets[v->dom.pre]) {
ssaBlock *u = ssa_lt_eval(&lt, v);
if (lt.sdom[u->index]->dom.pre < i) {
v->dom.idom = u;
} else {
v->dom.idom = w;
}
}
// Step 2 - Compute all sdoms
lt.sdom[w->index] = lt.parent[w->index];
for_array(pred_index, w->preds) {
ssaBlock *v = w->preds.e[pred_index];
ssaBlock *u = ssa_lt_eval(&lt, v);
if (lt.sdom[u->index]->dom.pre < lt.sdom[w->index]->dom.pre) {
lt.sdom[w->index] = lt.sdom[u->index];
}
}
ssa_lt_link(&lt, lt.parent[w->index], w);
if (lt.parent[w->index] == lt.sdom[w->index]) {
w->dom.idom = lt.parent[w->index];
} else {
buckets[i] = buckets[lt.sdom[w->index]->dom.pre];
buckets[lt.sdom[w->index]->dom.pre] = w;
}
}
// The rest of Step 3
for (ssaBlock *v = buckets[0]; v != root; v = buckets[v->dom.pre]) {
v->dom.idom = root;
}
// Step 4 - Explicitly define idom for nodes (in preorder)
for (isize i = 1; i < n; i++) {
ssaBlock *w = preorder[i];
if (w == root) {
w->dom.idom = NULL;
} else {
// Weird tree relationships here!
if (w->dom.idom != lt.sdom[w->index]) {
w->dom.idom = w->dom.idom->dom.idom;
}
// Calculate children relation as inverse of idom
if (w->dom.idom->dom.children.e == NULL) {
// TODO(bill): Is this good enough for memory allocations?
array_init(&w->dom.idom->dom.children, heap_allocator());
}
array_add(&w->dom.idom->dom.children, w);
}
}
ssa_opt_number_dom_tree(root, 0, 0);
gb_temp_arena_memory_end(tmp);
}
void ssa_opt_mem2reg(ssaProcedure *proc) {
// TODO(bill): ssa_opt_mem2reg
}
void ssa_opt_tree(ssaGen *s) {
s->opt_called = true;
for_array(member_index, s->module.procs) {
ssaProcedure *proc = s->module.procs.e[member_index];
if (proc->blocks.count == 0) { // Prototype/external procedure
continue;
}
ssa_opt_blocks(proc);
#if 1
ssa_opt_build_referrers(proc);
ssa_opt_build_dom_tree(proc);
// TODO(bill): ssa optimization
// [ ] cse (common-subexpression) elim
// [ ] copy elim
// [ ] dead code elim
// [ ] dead store/load elim
// [ ] phi elim
// [ ] short circuit elim
// [ ] bounds check elim
// [ ] lift/mem2reg
// [ ] lift/mem2reg
ssa_opt_mem2reg(proc);
#endif
GB_ASSERT(proc->blocks.count > 0);
ssa_number_proc_registers(proc);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,422 +0,0 @@
gb_global gbArena string_buffer_arena = {0};
gb_global gbAllocator string_buffer_allocator = {0};
void init_string_buffer_memory(void) {
// NOTE(bill): This should be enough memory for file systems
gb_arena_init_from_allocator(&string_buffer_arena, heap_allocator(), gb_megabytes(1));
string_buffer_allocator = gb_arena_allocator(&string_buffer_arena);
}
// NOTE(bill): Used for UTF-8 strings
typedef struct String {
u8 * text;
isize len;
} String;
// NOTE(bill): used for printf style arguments
#define LIT(x) ((int)(x).len), (x).text
typedef struct String16 {
wchar_t *text;
isize len;
} String16;
gb_inline String make_string(u8 *text, isize len) {
String s;
s.text = text;
if (len < 0) {
len = gb_strlen(cast(char *)text);
}
s.len = len;
return s;
}
gb_inline String16 make_string16(wchar_t *text, isize len) {
String16 s;
s.text = text;
s.len = len;
return s;
}
gb_inline String make_string_c(char *text) {
return make_string(cast(u8 *)cast(void *)text, gb_strlen(text));
}
#define str_lit(c_str) make_string(cast(u8 *)c_str, gb_size_of(c_str)-1)
gb_inline bool are_strings_equal(String a, String b) {
if (a.len == b.len) {
return gb_memcompare(a.text, b.text, a.len) == 0;
}
return false;
}
gb_inline bool str_eq_ignore_case(String a, String b) {
if (a.len == b.len) {
for (isize i = 0; i < a.len; i++) {
char x = cast(char)a.text[i];
char y = cast(char)b.text[i];
if (gb_char_to_lower(x) != gb_char_to_lower(y))
return false;
}
return true;
}
return false;
}
int string_compare(String x, String y) {
if (x.len == y.len &&
x.text == y.text) {
return 0;
}
isize n = gb_min(x.len, y.len);
isize fast = n/gb_size_of(isize) + 1;
isize offset = (fast-1)*gb_size_of(isize);
isize curr_block = 0;
if (n <= gb_size_of(isize)) {
fast = 0;
}
isize *la = cast(isize *)x.text;
isize *lb = cast(isize *)y.text;
for (; curr_block < fast; curr_block++) {
if (la[curr_block] ^ lb[curr_block]) {
for (isize pos = curr_block*gb_size_of(isize); pos < n; pos++) {
if (x.text[pos] ^ y.text[pos]) {
return cast(int)x.text[pos] - cast(int)y.text[pos];
}
}
}
}
for (; offset < n; offset++) {
if (x.text[offset] ^ y.text[offset]) {
return cast(int)x.text[offset] - cast(int)y.text[offset];
}
}
return 0;
}
GB_COMPARE_PROC(string_cmp_proc) {
String x = *(String *)a;
String y = *(String *)b;
return string_compare(x, y);
}
// gb_inline bool operator ==(String a, String b) { return are_strings_equal(a, b) != 0; }
// gb_inline bool operator !=(String a, String b) { return !operator==(a, b); }
// gb_inline bool operator < (String a, String b) { return string_compare(a, b) < 0; }
// gb_inline bool operator > (String a, String b) { return string_compare(a, b) > 0; }
// gb_inline bool operator <=(String a, String b) { return string_compare(a, b) <= 0; }
// gb_inline bool operator >=(String a, String b) { return string_compare(a, b) >= 0; }
// template <size_t N> gb_inline bool operator ==(String a, char const (&b)[N]) { return a == make_string(cast(u8 *)b, N-1); }
// template <size_t N> gb_inline bool operator !=(String a, char const (&b)[N]) { return a != make_string(cast(u8 *)b, N-1); }
// template <size_t N> gb_inline bool operator ==(char const (&a)[N], String b) { return make_string(cast(u8 *)a, N-1) == b; }
// template <size_t N> gb_inline bool operator !=(char const (&a)[N], String b) { return make_string(cast(u8 *)a, N-1) != b; }
gb_inline bool str_eq(String a, String b) { return are_strings_equal(a, b) != 0; }
gb_inline bool str_ne(String a, String b) { return !str_eq(a, b); }
gb_inline bool str_lt(String a, String b) { return string_compare(a, b) < 0; }
gb_inline bool str_gt(String a, String b) { return string_compare(a, b) > 0; }
gb_inline bool str_le(String a, String b) { return string_compare(a, b) <= 0; }
gb_inline bool str_ge(String a, String b) { return string_compare(a, b) >= 0; }
gb_inline isize string_extension_position(String str) {
isize dot_pos = -1;
isize i = str.len;
bool seen_dot = false;
while (i --> 0) {
if (str.text[i] == GB_PATH_SEPARATOR)
break;
if (str.text[i] == '.') {
dot_pos = i;
break;
}
}
return dot_pos;
}
gb_inline bool string_has_extension(String str, String ext) {
if (str.len > ext.len+1) {
u8 *s = str.text+str.len - ext.len-1;
if (s[0] == '.') {
s++;
return gb_memcompare(s, ext.text, ext.len) == 0;
}
return false;
}
return false;
}
bool string_contains_char(String s, u8 c) {
for (isize i = 0; i < s.len; i++) {
if (s.text[i] == c)
return true;
}
return false;
}
// TODO(bill): Make this non-windows specific
String16 string_to_string16(gbAllocator a, String s) {
if (s.len < 1) {
return make_string16(NULL, 0);
}
int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
cast(char *)s.text, s.len, NULL, 0);
if (len == 0) {
return make_string16(NULL, 0);
}
wchar_t *text = gb_alloc_array(a, wchar_t, len+1);
int len1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
cast(char *)s.text, s.len, text, len);
if (len1 == 0) {
gb_free(a, text);
return make_string16(NULL, 0);
}
text[len] = 0;
return make_string16(text, len-1);
}
String string16_to_string(gbAllocator a, String16 s) {
if (s.len < 1) {
return make_string(NULL, 0);
}
int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
s.text, s.len, NULL, 0,
NULL, NULL);
if (len == 0) {
return make_string(NULL, 0);
}
u8 *text = gb_alloc_array(a, u8, len+1);
int len1 = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
s.text, s.len, cast(char *)text, len,
NULL, NULL);
if (len1 == 0) {
gb_free(a, text);
return make_string(NULL, 0);
}
text[len] = 0;
return make_string(text, len-1);
}
bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *tail_string) {
if (s.text[0] == quote &&
(quote == '$' || quote == '"')) {
return false;
} else if (s.text[0] >= 0x80) {
Rune r = -1;
isize size = gb_utf8_decode(s.text, s.len, &r);
*rune = r;
*multiple_bytes = true;
*tail_string = make_string(s.text+size, s.len-size);
return true;
} else if (s.text[0] != '\\') {
*rune = s.text[0];
*tail_string = make_string(s.text+1, s.len-1);
return true;
}
if (s.len <= 1) {
return false;
}
u8 c = s.text[1];
s = make_string(s.text+2, s.len-2);
switch (c) {
default: return false;
case 'a': *rune = '\a'; break;
case 'b': *rune = '\b'; break;
case 'f': *rune = '\f'; break;
case 'n': *rune = '\n'; break;
case 'r': *rune = '\r'; break;
case 't': *rune = '\t'; break;
case 'v': *rune = '\v'; break;
case '\\': *rune = '\\'; break;
case '$':
case '"':
if (c != quote) {
return false;
}
*rune = c;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7': {
i32 r = gb_digit_to_int(c);
if (s.len < 2) {
return false;
}
for (isize i = 0; i < 2; i++) {
i32 d = gb_digit_to_int(s.text[i]);
if (d < 0 || d > 7) {
return false;
}
r = (r<<3) | d;
}
s = make_string(s.text+2, s.len-2);
if (r > 0xff) {
return false;
}
*rune = r;
} break;
case 'x':
case 'u':
case 'U': {
isize count = 0;
switch (c) {
case 'x': count = 2; break;
case 'u': count = 4; break;
case 'U': count = 8; break;
}
Rune r = 0;
if (s.len < count) {
return false;
}
for (isize i = 0; i < count; i++) {
i32 d = gb_hex_digit_to_int(s.text[i]);
if (d < 0) {
return false;
}
r = (r<<4) | d;
}
s = make_string(s.text+count, s.len-count);
if (c == 'x') {
*rune = r;
break;
}
if (r > GB_RUNE_MAX) {
return false;
}
*rune = r;
*multiple_bytes = true;
} break;
}
*tail_string = s;
return true;
}
// 0 == failure
// 1 == original memory
// 2 == new allocation
i32 unquote_string(gbAllocator a, String *s_) {
GB_ASSERT(s_ != NULL);
String s = *s_;
isize n = s.len;
if (n < 2)
return 0;
u8 quote = s.text[0];
if (quote != s.text[n-1])
return 0;
s.text += 1;
s.len -= 2;
if (quote == '`') {
if (string_contains_char(s, '`')) {
return 0;
}
*s_ = s;
return 1;
}
if (quote != '"' && quote != '$')
return 0;
if (string_contains_char(s, '\n'))
return 0;
if (!string_contains_char(s, '\\') && !string_contains_char(s, quote)) {
if (quote == '"') {
*s_ = s;
return 1;
} else if (quote == '$') {
Rune r = GB_RUNE_INVALID;
isize size = gb_utf8_decode(s.text, s.len, &r);
if ((size == s.len) && (r != -1 || size != 1)) {
*s_ = s;
return 1;
}
}
}
u8 rune_temp[4] = {0};
isize buf_len = 3*s.len / 2;
u8 *buf = gb_alloc_array(a, u8, buf_len);
isize offset = 0;
while (s.len > 0) {
String tail_string = {0};
Rune r = 0;
bool multiple_bytes = false;
bool success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string);
if (!success) {
gb_free(a, buf);
return 0;
}
s = tail_string;
if (r < 0x80 || !multiple_bytes) {
buf[offset++] = cast(u8)r;
} else {
isize size = gb_utf8_encode_rune(rune_temp, r);
gb_memmove(buf+offset, rune_temp, size);
offset += size;
}
if (quote == '$' && s.len != 0) {
gb_free(a, buf);
return 0;
}
}
*s_ = make_string(buf, offset);
return 2;
}

View File

@@ -1,105 +0,0 @@
typedef struct TimeStamp {
u64 start;
u64 finish;
String label;
} TimeStamp;
typedef struct Timings {
TimeStamp total;
Array(TimeStamp) sections;
u64 freq;
} Timings;
u64 win32_time_stamp_time_now(void) {
LARGE_INTEGER counter;
QueryPerformanceCounter(&counter);
return counter.QuadPart;
}
u64 win32_time_stamp__freq(void) {
gb_local_persist LARGE_INTEGER win32_perf_count_freq = {0};
if (!win32_perf_count_freq.QuadPart) {
QueryPerformanceFrequency(&win32_perf_count_freq);
GB_ASSERT(win32_perf_count_freq.QuadPart != 0);
}
return win32_perf_count_freq.QuadPart;
}
u64 time_stamp_time_now(void) {
#if defined(GB_SYSTEM_WINDOWS)
return win32_time_stamp_time_now();
#else
#error time_stamp_time_now
#endif
}
u64 time_stamp__freq(void) {
#if defined(GB_SYSTEM_WINDOWS)
return win32_time_stamp__freq();
#else
#error time_stamp__freq
#endif
}
TimeStamp make_time_stamp(String label) {
TimeStamp ts = {0};
ts.start = time_stamp_time_now();
ts.label = label;
return ts;
}
void timings_init(Timings *t, String label, isize buffer_size) {
array_init_reserve(&t->sections, heap_allocator(), buffer_size);
t->total = make_time_stamp(label);
t->freq = time_stamp__freq();
}
void timings_destroy(Timings *t) {
array_free(&t->sections);
}
void timings__stop_current_section(Timings *t) {
if (t->sections.count > 0) {
t->sections.e[t->sections.count-1].finish = time_stamp_time_now();
}
}
void timings_start_section(Timings *t, String label) {
timings__stop_current_section(t);
array_add(&t->sections, make_time_stamp(label));
}
f64 time_stamp_as_ms(TimeStamp ts, u64 freq) {
GB_ASSERT_MSG(ts.finish >= ts.start, "time_stamp_as_ms - %.*s", LIT(ts.label));
return 1000.0 * cast(f64)(ts.finish - ts.start) / cast(f64)freq;
}
void timings_print_all(Timings *t) {
timings__stop_current_section(t);
t->total.finish = time_stamp_time_now();
char const SPACES[] = " ";
isize max_len = t->total.label.len;
for_array(i, t->sections) {
TimeStamp ts = t->sections.e[i];
max_len = gb_max(max_len, ts.label.len);
}
GB_ASSERT(max_len <= gb_size_of(SPACES)-1);
gb_printf("%.*s%.*s - %.3f ms\n",
LIT(t->total.label),
cast(int)(max_len-t->total.label.len), SPACES,
time_stamp_as_ms(t->total, t->freq));
for_array(i, t->sections) {
TimeStamp ts = t->sections.e[i];
gb_printf("%.*s%.*s - %.3f ms\n",
LIT(ts.label),
cast(int)(max_len-ts.label.len), SPACES,
time_stamp_as_ms(ts, t->freq));
}
}

View File

@@ -1,816 +0,0 @@
#define TOKEN_KINDS \
TOKEN_KIND(Token_Invalid, "Invalid"), \
TOKEN_KIND(Token_EOF, "EOF"), \
TOKEN_KIND(Token_Comment, "Comment"), \
\
TOKEN_KIND(Token__LiteralBegin, "_LiteralBegin"), \
TOKEN_KIND(Token_Identifier, "Identifier"), \
TOKEN_KIND(Token_Integer, "Integer"), \
TOKEN_KIND(Token_Float, "Float"), \
TOKEN_KIND(Token_Rune, "Rune"), \
TOKEN_KIND(Token_String, "String"), \
TOKEN_KIND(Token__LiteralEnd, "_LiteralEnd"), \
\
TOKEN_KIND(Token__OperatorBegin, "_OperatorBegin"), \
TOKEN_KIND(Token_Eq, "="), \
TOKEN_KIND(Token_Not, "!"), \
TOKEN_KIND(Token_Hash, "#"), \
TOKEN_KIND(Token_At, "@"), \
TOKEN_KIND(Token_Pointer, "^"), \
TOKEN_KIND(Token_Maybe, "?"), \
TOKEN_KIND(Token_Add, "+"), \
TOKEN_KIND(Token_Sub, "-"), \
TOKEN_KIND(Token_Mul, "*"), \
TOKEN_KIND(Token_Quo, "/"), \
TOKEN_KIND(Token_Mod, "%"), \
TOKEN_KIND(Token_And, "&"), \
TOKEN_KIND(Token_Or, "|"), \
TOKEN_KIND(Token_Xor, "~"), \
TOKEN_KIND(Token_AndNot, "&~"), \
TOKEN_KIND(Token_Shl, "<<"), \
TOKEN_KIND(Token_Shr, ">>"), \
\
TOKEN_KIND(Token_as, "as"), \
TOKEN_KIND(Token_transmute, "transmute"), \
TOKEN_KIND(Token_down_cast, "down_cast"), \
TOKEN_KIND(Token_union_cast, "union_cast"), \
\
TOKEN_KIND(Token_Prime, "'"), \
TOKEN_KIND(Token_DoublePrime, "''"), \
\
TOKEN_KIND(Token_CmpAnd, "&&"), \
TOKEN_KIND(Token_CmpOr, "||"), \
\
TOKEN_KIND(Token__AssignOpBegin, "_AssignOpBegin"), \
TOKEN_KIND(Token_AddEq, "+="), \
TOKEN_KIND(Token_SubEq, "-="), \
TOKEN_KIND(Token_MulEq, "*="), \
TOKEN_KIND(Token_QuoEq, "/="), \
TOKEN_KIND(Token_ModEq, "%="), \
TOKEN_KIND(Token_AndEq, "&="), \
TOKEN_KIND(Token_OrEq, "|="), \
TOKEN_KIND(Token_XorEq, "~="), \
TOKEN_KIND(Token_AndNotEq, "&~="), \
TOKEN_KIND(Token_ShlEq, "<<="), \
TOKEN_KIND(Token_ShrEq, ">>="), \
TOKEN_KIND(Token_CmpAndEq, "&&="), \
TOKEN_KIND(Token_CmpOrEq, "||="), \
TOKEN_KIND(Token__AssignOpEnd, "_AssignOpEnd"), \
TOKEN_KIND(Token_Increment, "++"), \
TOKEN_KIND(Token_Decrement, "--"), \
TOKEN_KIND(Token_ArrowRight, "->"), \
TOKEN_KIND(Token_ArrowLeft, "<-"), \
\
TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \
TOKEN_KIND(Token_CmpEq, "=="), \
TOKEN_KIND(Token_NotEq, "!="), \
TOKEN_KIND(Token_Lt, "<"), \
TOKEN_KIND(Token_Gt, ">"), \
TOKEN_KIND(Token_LtEq, "<="), \
TOKEN_KIND(Token_GtEq, ">="), \
TOKEN_KIND(Token__ComparisonEnd, "_ComparisonEnd"), \
\
TOKEN_KIND(Token_OpenParen, "("), \
TOKEN_KIND(Token_CloseParen, ")"), \
TOKEN_KIND(Token_OpenBracket, "["), \
TOKEN_KIND(Token_CloseBracket, "]"), \
TOKEN_KIND(Token_OpenBrace, "{"), \
TOKEN_KIND(Token_CloseBrace, "}"), \
TOKEN_KIND(Token_Colon, ":"), \
TOKEN_KIND(Token_Semicolon, ";"), \
TOKEN_KIND(Token_Period, "."), \
TOKEN_KIND(Token_Comma, ","), \
TOKEN_KIND(Token_Ellipsis, ".."), \
TOKEN_KIND(Token_RangeExclusive, "..<"), \
TOKEN_KIND(Token__OperatorEnd, "_OperatorEnd"), \
\
TOKEN_KIND(Token__KeywordBegin, "_KeywordBegin"), \
TOKEN_KIND(Token_type, "type"), \
TOKEN_KIND(Token_proc, "proc"), \
TOKEN_KIND(Token_match, "match"), \
TOKEN_KIND(Token_break, "break"), \
TOKEN_KIND(Token_continue, "continue"), \
TOKEN_KIND(Token_fallthrough, "fallthrough"), \
TOKEN_KIND(Token_case, "case"), \
TOKEN_KIND(Token_default, "default"), \
TOKEN_KIND(Token_then, "then"), \
TOKEN_KIND(Token_if, "if"), \
TOKEN_KIND(Token_else, "else"), \
TOKEN_KIND(Token_for, "for"), \
TOKEN_KIND(Token_range, "range"), \
TOKEN_KIND(Token_defer, "defer"), \
TOKEN_KIND(Token_return, "return"), \
TOKEN_KIND(Token_struct, "struct"), \
TOKEN_KIND(Token_union, "union"), \
TOKEN_KIND(Token_raw_union, "raw_union"), \
TOKEN_KIND(Token_enum, "enum"), \
TOKEN_KIND(Token_using, "using"), \
TOKEN_KIND(Token_asm, "asm"), \
TOKEN_KIND(Token_volatile, "volatile"), \
TOKEN_KIND(Token_atomic, "atomic"), \
TOKEN_KIND(Token_push_allocator, "push_allocator"), \
TOKEN_KIND(Token_push_context, "push_context"), \
TOKEN_KIND(Token__KeywordEnd, "_KeywordEnd"), \
TOKEN_KIND(Token_Count, "")
typedef enum TokenKind {
#define TOKEN_KIND(e, s) e
TOKEN_KINDS
#undef TOKEN_KIND
} TokenKind;
String const token_strings[] = {
#define TOKEN_KIND(e, s) {cast(u8 *)s, gb_size_of(s)-1}
TOKEN_KINDS
#undef TOKEN_KIND
};
typedef struct TokenPos {
String file;
isize line;
isize column;
} TokenPos;
i32 token_pos_cmp(TokenPos a, TokenPos b) {
if (a.line == b.line) {
if (a.column == b.column) {
isize min_len = gb_min(a.file.len, b.file.len);
return gb_memcompare(a.file.text, b.file.text, min_len);
}
return (a.column < b.column) ? -1 : +1;
}
return (a.line < b.line) ? -1 : +1;
}
bool token_pos_are_equal(TokenPos a, TokenPos b) {
return token_pos_cmp(a, b) == 0;
}
// NOTE(bill): Text is UTF-8, thus why u8 and not char
typedef struct Token {
TokenKind kind;
String string;
TokenPos pos;
} Token;
Token empty_token = {Token_Invalid};
Token blank_token = {Token_Identifier, {cast(u8 *)"_", 1}};
Token make_token_ident(String s) {
Token t = {Token_Identifier, s};
return t;
}
typedef struct ErrorCollector {
TokenPos prev;
i64 count;
i64 warning_count;
gbMutex mutex;
} ErrorCollector;
gb_global ErrorCollector global_error_collector;
void init_global_error_collector(void) {
gb_mutex_init(&global_error_collector.mutex);
}
void warning(Token token, char *fmt, ...) {
gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.warning_count++;
// NOTE(bill): Duplicate error, skip it
if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
va_list va;
global_error_collector.prev = token.pos;
va_start(va, fmt);
gb_printf_err("%.*s(%td:%td) Warning: %s\n",
LIT(token.pos.file), token.pos.line, token.pos.column,
gb_bprintf_va(fmt, va));
va_end(va);
}
gb_mutex_unlock(&global_error_collector.mutex);
}
void error(Token token, char *fmt, ...) {
gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.count++;
// NOTE(bill): Duplicate error, skip it
if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
va_list va;
global_error_collector.prev = token.pos;
va_start(va, fmt);
gb_printf_err("%.*s(%td:%td) %s\n",
LIT(token.pos.file), token.pos.line, token.pos.column,
gb_bprintf_va(fmt, va));
va_end(va);
}
gb_mutex_unlock(&global_error_collector.mutex);
}
void syntax_error(Token token, char *fmt, ...) {
gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.count++;
// NOTE(bill): Duplicate error, skip it
if (!token_pos_are_equal(global_error_collector.prev, token.pos)) {
va_list va;
global_error_collector.prev = token.pos;
va_start(va, fmt);
gb_printf_err("%.*s(%td:%td) Syntax Error: %s\n",
LIT(token.pos.file), token.pos.line, token.pos.column,
gb_bprintf_va(fmt, va));
va_end(va);
}
gb_mutex_unlock(&global_error_collector.mutex);
}
void compiler_error(char *fmt, ...) {
va_list va;
va_start(va, fmt);
gb_printf_err("Internal Compiler Error: %s\n",
gb_bprintf_va(fmt, va));
va_end(va);
gb_exit(1);
}
gb_inline bool token_is_literal(Token t) {
return gb_is_between(t.kind, Token__LiteralBegin+1, Token__LiteralEnd-1);
}
gb_inline bool token_is_operator(Token t) {
return gb_is_between(t.kind, Token__OperatorBegin+1, Token__OperatorEnd-1);
}
gb_inline bool token_is_keyword(Token t) {
return gb_is_between(t.kind, Token__KeywordBegin+1, Token__KeywordEnd-1);
}
gb_inline bool token_is_comparison(Token t) {
return gb_is_between(t.kind, Token__ComparisonBegin+1, Token__ComparisonEnd-1);
}
gb_inline bool token_is_shift(Token t) {
return t.kind == Token_Shl || t.kind == Token_Shr;
}
gb_inline void print_token(Token t) { gb_printf("%.*s\n", LIT(t.string)); }
typedef enum TokenizerInitError {
TokenizerInit_None,
TokenizerInit_Invalid,
TokenizerInit_NotExists,
TokenizerInit_Permission,
TokenizerInit_Empty,
TokenizerInit_Count,
} TokenizerInitError;
typedef struct Tokenizer {
String fullpath;
u8 *start;
u8 *end;
Rune curr_rune; // current character
u8 * curr; // character pos
u8 * read_curr; // pos from start
u8 * line; // current line pos
isize line_count;
isize error_count;
Array(String) allocated_strings;
} Tokenizer;
void tokenizer_err(Tokenizer *t, char *msg, ...) {
va_list va;
isize column = t->read_curr - t->line+1;
if (column < 1)
column = 1;
gb_printf_err("%.*s(%td:%td) Syntax error: ", LIT(t->fullpath), t->line_count, column);
va_start(va, msg);
gb_printf_err_va(msg, va);
va_end(va);
gb_printf_err("\n");
t->error_count++;
}
void advance_to_next_rune(Tokenizer *t) {
if (t->read_curr < t->end) {
Rune rune;
isize width = 1;
t->curr = t->read_curr;
if (t->curr_rune == '\n') {
t->line = t->curr;
t->line_count++;
}
rune = *t->read_curr;
if (rune == 0) {
tokenizer_err(t, "Illegal character NUL");
} else if (rune >= 0x80) { // not ASCII
width = gb_utf8_decode(t->read_curr, t->end-t->read_curr, &rune);
if (rune == GB_RUNE_INVALID && width == 1)
tokenizer_err(t, "Illegal UTF-8 encoding");
else if (rune == GB_RUNE_BOM && t->curr-t->start > 0)
tokenizer_err(t, "Illegal byte order mark");
}
t->read_curr += width;
t->curr_rune = rune;
} else {
t->curr = t->end;
if (t->curr_rune == '\n') {
t->line = t->curr;
t->line_count++;
}
t->curr_rune = GB_RUNE_EOF;
}
}
TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
TokenizerInitError err = TokenizerInit_None;
char *c_str = gb_alloc_array(heap_allocator(), char, fullpath.len+1);
memcpy(c_str, fullpath.text, fullpath.len);
c_str[fullpath.len] = '\0';
// TODO(bill): Memory map rather than copy contents
gbFileContents fc = gb_file_read_contents(heap_allocator(), true, c_str);
gb_zero_item(t);
if (fc.data != NULL) {
t->start = cast(u8 *)fc.data;
t->line = t->read_curr = t->curr = t->start;
t->end = t->start + fc.size;
t->fullpath = fullpath;
t->line_count = 1;
advance_to_next_rune(t);
if (t->curr_rune == GB_RUNE_BOM) {
advance_to_next_rune(t); // Ignore BOM at file beginning
}
array_init(&t->allocated_strings, heap_allocator());
} else {
gbFile f = {0};
gbFileError file_err = gb_file_open(&f, c_str);
switch (file_err) {
case gbFileError_Invalid: err = TokenizerInit_Invalid; break;
case gbFileError_NotExists: err = TokenizerInit_NotExists; break;
case gbFileError_Permission: err = TokenizerInit_Permission; break;
}
if (err == TokenizerInit_None && gb_file_size(&f) == 0) {
err = TokenizerInit_Empty;
}
gb_file_close(&f);
}
gb_free(heap_allocator(), c_str);
return err;
}
gb_inline void destroy_tokenizer(Tokenizer *t) {
if (t->start != NULL) {
gb_free(heap_allocator(), t->start);
}
for_array(i, t->allocated_strings) {
gb_free(heap_allocator(), t->allocated_strings.e[i].text);
}
array_free(&t->allocated_strings);
}
void tokenizer_skip_whitespace(Tokenizer *t) {
while (rune_is_whitespace(t->curr_rune)) {
advance_to_next_rune(t);
}
}
gb_inline i32 digit_value(Rune r) {
if (gb_char_is_digit(cast(char)r)) {
return r - '0';
} else if (gb_is_between(cast(char)r, 'a', 'f')) {
return r - 'a' + 10;
} else if (gb_is_between(cast(char)r, 'A', 'F')) {
return r - 'A' + 10;
}
return 16; // NOTE(bill): Larger than highest possible
}
gb_inline void scan_mantissa(Tokenizer *t, i32 base) {
// TODO(bill): Allow for underscores in numbers as a number separator
// TODO(bill): Is this a good idea?
// while (digit_value(t->curr_rune) < base || t->curr_rune == '_')
while (digit_value(t->curr_rune) < base) {
advance_to_next_rune(t);
}
}
Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) {
Token token = {0};
token.kind = Token_Integer;
token.string = make_string(t->curr, 1);
token.pos.file = t->fullpath;
token.pos.line = t->line_count;
token.pos.column = t->curr-t->line+1;
if (seen_decimal_point) {
token.kind = Token_Float;
scan_mantissa(t, 10);
goto exponent;
}
if (t->curr_rune == '0') {
u8 *prev = t->curr;
advance_to_next_rune(t);
if (t->curr_rune == 'b') { // Binary
advance_to_next_rune(t);
scan_mantissa(t, 2);
if (t->curr - prev <= 2)
token.kind = Token_Invalid;
} else if (t->curr_rune == 'o') { // Octal
advance_to_next_rune(t);
scan_mantissa(t, 8);
if (t->curr - prev <= 2)
token.kind = Token_Invalid;
} else if (t->curr_rune == 'd') { // Decimal
advance_to_next_rune(t);
scan_mantissa(t, 10);
if (t->curr - prev <= 2)
token.kind = Token_Invalid;
} else if (t->curr_rune == 'x') { // Hexadecimal
advance_to_next_rune(t);
scan_mantissa(t, 16);
if (t->curr - prev <= 2)
token.kind = Token_Invalid;
} else {
seen_decimal_point = false;
scan_mantissa(t, 10);
if (t->curr_rune == '.' || t->curr_rune == 'e' || t->curr_rune == 'E') {
seen_decimal_point = true;
goto fraction;
}
}
token.string.len = t->curr - token.string.text;
return token;
}
scan_mantissa(t, 10);
fraction:
if (t->curr_rune == '.') {
token.kind = Token_Float;
advance_to_next_rune(t);
scan_mantissa(t, 10);
}
exponent:
if (t->curr_rune == 'e' || t->curr_rune == 'E') {
token.kind = Token_Float;
advance_to_next_rune(t);
if (t->curr_rune == '-' || t->curr_rune == '+') {
advance_to_next_rune(t);
}
scan_mantissa(t, 10);
}
token.string.len = t->curr - token.string.text;
return token;
}
// Quote == " for string
bool scan_escape(Tokenizer *t, Rune quote) {
isize len = 0;
u32 base = 0, max = 0, x = 0;
Rune r = t->curr_rune;
if (r == 'a' ||
r == 'b' ||
r == 'f' ||
r == 'n' ||
r == 'r' ||
r == 't' ||
r == 'v' ||
r == '\\' ||
r == quote) {
advance_to_next_rune(t);
return true;
} else if (gb_is_between(r, '0', '7')) {
len = 3; base = 8; max = 255;
} else if (r == 'x') {
advance_to_next_rune(t);
len = 2; base = 16; max = 255;
} else if (r == 'u') {
advance_to_next_rune(t);
len = 4; base = 16; max = GB_RUNE_MAX;
} else if (r == 'U') {
advance_to_next_rune(t);
len = 8; base = 16; max = GB_RUNE_MAX;
} else {
if (t->curr_rune < 0)
tokenizer_err(t, "Escape sequence was not terminated");
else
tokenizer_err(t, "Unknown escape sequence");
return false;
}
while (len --> 0) {
u32 d = cast(u32)digit_value(t->curr_rune);
if (d >= base) {
if (t->curr_rune < 0)
tokenizer_err(t, "Escape sequence was not terminated");
else
tokenizer_err(t, "Illegal character %d in escape sequence", t->curr_rune);
return false;
}
x = x*base + d;
advance_to_next_rune(t);
}
return true;
}
gb_inline TokenKind token_kind_variant2(Tokenizer *t, TokenKind a, TokenKind b) {
if (t->curr_rune == '=') {
advance_to_next_rune(t);
return b;
}
return a;
}
gb_inline TokenKind token_kind_variant3(Tokenizer *t, TokenKind a, TokenKind b, Rune ch_c, TokenKind c) {
if (t->curr_rune == '=') {
advance_to_next_rune(t);
return b;
}
if (t->curr_rune == ch_c) {
advance_to_next_rune(t);
return c;
}
return a;
}
gb_inline TokenKind token_kind_variant4(Tokenizer *t, TokenKind a, TokenKind b, Rune ch_c, TokenKind c, Rune ch_d, TokenKind d) {
if (t->curr_rune == '=') {
advance_to_next_rune(t);
return b;
} else if (t->curr_rune == ch_c) {
advance_to_next_rune(t);
return c;
} else if (t->curr_rune == ch_d) {
advance_to_next_rune(t);
return d;
}
return a;
}
gb_inline TokenKind token_kind_dub_eq(Tokenizer *t, Rune sing_rune, TokenKind sing, TokenKind sing_eq, TokenKind dub, TokenKind dub_eq) {
if (t->curr_rune == '=') {
advance_to_next_rune(t);
return sing_eq;
} else if (t->curr_rune == sing_rune) {
advance_to_next_rune(t);
if (t->curr_rune == '=') {
advance_to_next_rune(t);
return dub_eq;
}
return dub;
}
return sing;
}
Token tokenizer_get_token(Tokenizer *t) {
Token token = {0};
Rune curr_rune;
tokenizer_skip_whitespace(t);
token.string = make_string(t->curr, 1);
token.pos.file = t->fullpath;
token.pos.line = t->line_count;
token.pos.column = t->curr - t->line + 1;
curr_rune = t->curr_rune;
if (rune_is_letter(curr_rune)) {
token.kind = Token_Identifier;
while (rune_is_letter(t->curr_rune) || rune_is_digit(t->curr_rune)) {
advance_to_next_rune(t);
}
token.string.len = t->curr - token.string.text;
// NOTE(bill): All keywords are > 1
if (token.string.len > 1) {
if (str_eq(token.string, token_strings[Token_as])) {
token.kind = Token_as;
} else if (str_eq(token.string, token_strings[Token_transmute])) {
token.kind = Token_transmute;
} else if (str_eq(token.string, token_strings[Token_down_cast])) {
token.kind = Token_down_cast;
} else if (str_eq(token.string, token_strings[Token_union_cast])) {
token.kind = Token_union_cast;
} else {
for (i32 k = Token__KeywordBegin+1; k < Token__KeywordEnd; k++) {
if (str_eq(token.string, token_strings[k])) {
token.kind = cast(TokenKind)k;
break;
}
}
}
}
} else if (gb_is_between(curr_rune, '0', '9')) {
token = scan_number_to_token(t, false);
} else {
advance_to_next_rune(t);
switch (curr_rune) {
case GB_RUNE_EOF:
token.kind = Token_EOF;
break;
case '\'':
token.kind = Token_Prime;
if (t->curr_rune == '\'') {
advance_to_next_rune(t);
token.kind = Token_DoublePrime;
}
break;
case '`': // Raw String Literal
case '"': // String Literal
{
Rune quote = curr_rune;
token.kind = Token_String;
if (curr_rune == '"') {
for (;;) {
Rune r = t->curr_rune;
if (r == '\n' || r < 0) {
tokenizer_err(t, "String literal not terminated");
break;
}
advance_to_next_rune(t);
if (r == quote)
break;
if (r == '\\')
scan_escape(t, '"');
}
} else {
for (;;) {
Rune r = t->curr_rune;
if (r < 0) {
tokenizer_err(t, "String literal not terminated");
break;
}
advance_to_next_rune(t);
if (r == quote)
break;
}
}
token.string.len = t->curr - token.string.text;
i32 success = unquote_string(heap_allocator(), &token.string);
if (success > 0) {
if (success == 2) {
array_add(&t->allocated_strings, token.string);
}
return token;
} else {
tokenizer_err(t, "Invalid string literal");
}
} break;
case '.':
token.kind = Token_Period; // Default
if (gb_is_between(t->curr_rune, '0', '9')) { // Might be a number
token = scan_number_to_token(t, true);
} else if (t->curr_rune == '.') { // Could be an ellipsis
advance_to_next_rune(t);
token.kind = Token_Ellipsis;
if (t->curr_rune == '<') {
advance_to_next_rune(t);
token.kind = Token_RangeExclusive;
}
}
break;
case '#': token.kind = Token_Hash; break;
case '@': token.kind = Token_At; break;
case '^': token.kind = Token_Pointer; break;
case '?': token.kind = Token_Maybe; break;
case ';': token.kind = Token_Semicolon; break;
case ',': token.kind = Token_Comma; break;
case '(': token.kind = Token_OpenParen; break;
case ')': token.kind = Token_CloseParen; break;
case '[': token.kind = Token_OpenBracket; break;
case ']': token.kind = Token_CloseBracket; break;
case '{': token.kind = Token_OpenBrace; break;
case '}': token.kind = Token_CloseBrace; break;
case ':': token.kind = Token_Colon; break;
case '*': token.kind = token_kind_variant2(t, Token_Mul, Token_MulEq); break;
case '%': token.kind = token_kind_variant2(t, Token_Mod, Token_ModEq); break;
case '=': token.kind = token_kind_variant2(t, Token_Eq, Token_CmpEq); break;
case '~': token.kind = token_kind_variant2(t, Token_Xor, Token_XorEq); break;
case '!': token.kind = token_kind_variant2(t, Token_Not, Token_NotEq); break;
case '+': token.kind = token_kind_variant3(t, Token_Add, Token_AddEq, '+', Token_Increment); break;
case '-': token.kind = token_kind_variant4(t, Token_Sub, Token_SubEq, '-', Token_Decrement, '>', Token_ArrowRight); break;
case '/': {
if (t->curr_rune == '/') {
while (t->curr_rune != '\n') {
advance_to_next_rune(t);
}
token.kind = Token_Comment;
} else if (t->curr_rune == '*') {
isize comment_scope = 1;
advance_to_next_rune(t);
while (comment_scope > 0) {
if (t->curr_rune == '/') {
advance_to_next_rune(t);
if (t->curr_rune == '*') {
advance_to_next_rune(t);
comment_scope++;
}
} else if (t->curr_rune == '*') {
advance_to_next_rune(t);
if (t->curr_rune == '/') {
advance_to_next_rune(t);
comment_scope--;
}
} else {
advance_to_next_rune(t);
}
}
token.kind = Token_Comment;
} else {
token.kind = token_kind_variant2(t, Token_Quo, Token_QuoEq);
}
} break;
case '<':
if (t->curr_rune == '-') {
token.kind = Token_ArrowLeft;
} else {
token.kind = token_kind_dub_eq(t, '<', Token_Lt, Token_LtEq, Token_Shl, Token_ShlEq);
}
break;
case '>':
token.kind = token_kind_dub_eq(t, '>', Token_Gt, Token_GtEq, Token_Shr, Token_ShrEq);
break;
case '&':
token.kind = Token_And;
if (t->curr_rune == '~') {
token.kind = Token_AndNot;
advance_to_next_rune(t);
if (t->curr_rune == '=') {
token.kind = Token_AndNotEq;
advance_to_next_rune(t);
}
} else {
token.kind = token_kind_dub_eq(t, '&', Token_And, Token_AndEq, Token_CmpAnd, Token_CmpAndEq);
}
break;
case '|': token.kind = token_kind_dub_eq(t, '|', Token_Or, Token_OrEq, Token_CmpOr, Token_CmpOrEq); break;
default:
if (curr_rune != GB_RUNE_BOM) {
u8 str[4] = {0};
int len = cast(int)gb_utf8_encode_rune(str, curr_rune);
tokenizer_err(t, "Illegal character: %.*s (%d) ", len, str, curr_rune);
}
token.kind = Token_Invalid;
break;
}
}
token.string.len = t->curr - token.string.text;
return token;
}

View File

@@ -1,66 +0,0 @@
#pragma warning(push)
#pragma warning(disable: 4245)
// #include "utf8proc/utf8proc.h"
#include "utf8proc/utf8proc.c"
#pragma warning(pop)
bool rune_is_letter(Rune r) {
if ((r < 0x80 && gb_char_is_alpha(cast(char)r)) ||
r == '_') {
return true;
}
switch (utf8proc_category(r)) {
case UTF8PROC_CATEGORY_LU:
case UTF8PROC_CATEGORY_LL:
case UTF8PROC_CATEGORY_LT:
case UTF8PROC_CATEGORY_LM:
case UTF8PROC_CATEGORY_LO:
return true;
}
return false;
}
bool rune_is_digit(Rune r) {
if (r < 0x80 && gb_is_between(r, '0', '9')) {
return true;
}
return utf8proc_category(r) == UTF8PROC_CATEGORY_ND;
}
bool rune_is_whitespace(Rune r) {
switch (r) {
case ' ':
case '\t':
case '\n':
case '\r':
return true;
}
return false;
}
bool is_string_an_identifier(String s) {
if (s.len < 1) {
return false;
}
isize offset = 0;
while (offset < s.len) {
bool ok = false;
Rune r = -1;
isize size = gb_utf8_decode(s.text+offset, s.len-offset, &r);
if (offset == 0) {
ok = rune_is_letter(r);
} else {
ok = rune_is_letter(r) || rune_is_digit(r);
}
if (!ok) {
return false;
}
offset += size;
}
return offset == s.len;
}