Initial Commit

Original Git Repo "went bad" so I had to restart.
This commit is contained in:
gingerBill
2016-07-07 21:47:33 +01:00
commit 7430008fd7
20 changed files with 17332 additions and 0 deletions
+457
View File
@@ -0,0 +1,457 @@
enum AddressingMode {
Addressing_Invalid,
Addressing_NoValue,
Addressing_Value,
Addressing_Variable,
Addressing_Constant,
Addressing_Type,
Addressing_Builtin,
Addressing_Count,
};
struct Operand {
AddressingMode mode;
Type *type;
Value value;
AstNode *expression;
i32 builtin_id;
};
struct TypeAndValue {
AddressingMode mode;
Type *type;
Value value;
};
struct ExpressionInfo {
b32 is_lhs; // Debug info
AddressingMode mode;
Type *type; // Type_Basic
Value value;
};
ExpressionInfo make_expression_info(b32 is_lhs, AddressingMode mode, Type *type, Value value) {
ExpressionInfo ei = {};
ei.is_lhs = is_lhs;
ei.mode = mode;
ei.type = type;
ei.value = value;
return ei;
}
struct Scope {
Scope *parent;
gbArray(Scope *) children; // TODO(bill): Remove and make into a linked list
Map<Entity *> elements; // Key: String
};
enum ExpressionKind {
Expression_Expression,
Expression_Conversion,
Expression_Statement,
};
struct BuiltinProcedure {
String name;
isize arg_count;
b32 variadic;
ExpressionKind kind;
};
enum BuiltinProcedureId {
BuiltinProcedure_Invalid,
BuiltinProcedure_size_of,
BuiltinProcedure_size_of_val,
BuiltinProcedure_align_of,
BuiltinProcedure_align_of_val,
BuiltinProcedure_offset_of,
BuiltinProcedure_offset_of_val,
BuiltinProcedure_static_assert,
BuiltinProcedure_print,
BuiltinProcedure_println,
BuiltinProcedure_Count,
};
struct Checker {
Parser * parser;
Map<TypeAndValue> types; // Key: AstNode * | Expression -> Type (and value)
Map<Entity *> definitions; // Key: AstNode * | Identifier -> Entity
Map<Entity *> uses; // Key: AstNode * | Identifier -> Entity (Anonymous field)
Map<Scope *> scopes; // Key: AstNode * | Node -> Scope
Map<ExpressionInfo> untyped; // Key: AstNode * | Expression -> ExpressionInfo
BaseTypeSizes sizes;
Scope * file_scope;
gbArena entity_arena;
Scope *curr_scope;
gbArray(Type *) procedure_stack;
b32 in_defer;
#define MAX_CHECKER_ERROR_COUNT 10
isize error_prev_line;
isize error_prev_column;
isize error_count;
};
gb_global Scope *global_scope = NULL;
gb_global BuiltinProcedure builtin_procedures[BuiltinProcedure_Count] = {
{STR_LIT(""), 0, false, Expression_Statement},
{STR_LIT("size_of"), 1, false, Expression_Expression},
{STR_LIT("size_of_val"), 1, false, Expression_Expression},
{STR_LIT("align_of"), 1, false, Expression_Expression},
{STR_LIT("align_of_val"), 1, false, Expression_Expression},
{STR_LIT("offset_of"), 2, false, Expression_Expression},
{STR_LIT("offset_of_val"), 1, false, Expression_Expression},
{STR_LIT("static_assert"), 1, false, Expression_Statement},
{STR_LIT("print"), 1, true, Expression_Statement},
{STR_LIT("println"), 1, true, Expression_Statement},
};
// TODO(bill): Arena allocation
Scope *make_scope(Scope *parent) {
gbAllocator a = gb_heap_allocator();
Scope *s = gb_alloc_item(a, Scope);
s->parent = parent;
gb_array_init(s->children, a);
map_init(&s->elements, a);
if (parent != NULL && parent != global_scope)
gb_array_append(parent->children, s);
return s;
}
void destroy_scope(Scope *scope) {
for (isize i = 0; i < gb_array_count(scope->children); i++) {
destroy_scope(scope->children[i]);
}
map_destroy(&scope->elements);
gb_array_free(scope->children);
gb_free(gb_heap_allocator(), scope);
}
void scope_lookup_parent_entity(Scope *s, String name, Scope **scope, Entity **entity) {
u64 key = hash_string(name);
for (; s != NULL; s = s->parent) {
Entity **found = map_get(&s->elements, key);
if (found) {
if (entity) *entity = *found;
if (scope) *scope = s;
return;
}
}
if (entity) *entity = NULL;
if (scope) *scope = NULL;
}
Entity *scope_lookup_entity(Scope *s, String name) {
Entity *entity = NULL;
scope_lookup_parent_entity(s, name, NULL, &entity);
return entity;
}
Entity *scope_lookup_entity_current(Scope *s, String name) {
u64 key = hash_string(name);
Entity **found = map_get(&s->elements, key);
if (found)
return *found;
return NULL;
}
Entity *scope_insert_entity(Scope *s, Entity *entity) {
String name = entity->token.string;
u64 key = hash_string(name);
Entity **found = map_get(&s->elements, key);
if (found)
return *found;
map_set(&s->elements, key, entity);
if (entity->parent == NULL)
entity->parent = s;
return NULL;
}
void add_global_entity(Entity *entity) {
String name = entity->token.string;
if (gb_memchr(name.text, ' ', name.len)) {
return; // NOTE(bill): `untyped thing`
}
if (scope_insert_entity(global_scope, entity)) {
GB_PANIC("Internal type checking error: double declaration");
}
}
void init_global_scope(void) {
global_scope = make_scope(NULL);
gbAllocator a = gb_heap_allocator();
// Types
for (isize i = 0; i < gb_count_of(basic_types); i++) {
Token token = {Token_Identifier};
token.string = basic_types[i].basic.name;
add_global_entity(alloc_entity(a, Entity_TypeName, NULL, token, &basic_types[i]));
}
for (isize i = 0; i < gb_count_of(basic_type_aliases); i++) {
Token token = {Token_Identifier};
token.string = basic_type_aliases[i].basic.name;
add_global_entity(alloc_entity(a, Entity_TypeName, NULL, token, &basic_type_aliases[i]));
}
// Constants
Token true_token = {Token_Identifier};
true_token.string = make_string("true");
Entity *true_entity = alloc_entity(a, Entity_Constant, NULL, true_token, &basic_types[Basic_UntypedBool]);
true_entity->constant.value = make_value_bool(true);
add_global_entity(true_entity);
Token false_token = {Token_Identifier};
false_token.string = make_string("false");
Entity *false_entity = alloc_entity(a, Entity_Constant, NULL, false_token, &basic_types[Basic_UntypedBool]);
false_entity->constant.value = make_value_bool(false);
add_global_entity(false_entity);
Token null_token = {Token_Identifier};
null_token.string = make_string("null");
Entity *null_entity = alloc_entity(a, Entity_Constant, NULL, null_token, &basic_types[Basic_UntypedPointer]);
null_entity->constant.value = make_value_pointer(NULL);
add_global_entity(null_entity);
// Builtin Procedures
for (isize i = 0; i < gb_count_of(builtin_procedures); i++) {
i32 id = cast(i32)i;
Token token = {Token_Identifier};
token.string = builtin_procedures[i].name;
Entity *entity = alloc_entity(a, Entity_Builtin, NULL, token, &basic_types[Basic_Invalid]);
entity->builtin.id = id;
add_global_entity(entity);
}
}
void init_checker(Checker *c, Parser *parser) {
gbAllocator a = gb_heap_allocator();
c->parser = parser;
map_init(&c->types, gb_heap_allocator());
map_init(&c->definitions, gb_heap_allocator());
map_init(&c->uses, gb_heap_allocator());
map_init(&c->scopes, gb_heap_allocator());
c->sizes.word_size = 8;
c->sizes.max_align = 8;
map_init(&c->untyped, a);
c->file_scope = make_scope(global_scope);
c->curr_scope = c->file_scope;
gb_array_init(c->procedure_stack, a);
// NOTE(bill): Is this big enough or too small?
isize entity_arena_size = 2 * gb_size_of(Entity) * gb_array_count(c->parser->tokens);
gb_arena_init_from_allocator(&c->entity_arena, a, entity_arena_size);
}
void destroy_checker(Checker *c) {
map_destroy(&c->types);
map_destroy(&c->definitions);
map_destroy(&c->uses);
map_destroy(&c->scopes);
map_destroy(&c->untyped);
destroy_scope(c->file_scope);
gb_array_free(c->procedure_stack);
gb_arena_free(&c->entity_arena);
}
#define print_checker_error(p, token, fmt, ...) print_checker_error_(p, __FUNCTION__, token, fmt, ##__VA_ARGS__)
void print_checker_error_(Checker *c, char *function, Token token, char *fmt, ...) {
va_list va;
// NOTE(bill): Duplicate error, skip it
if (c->error_prev_line == token.line && c->error_prev_column == token.column) {
goto error;
}
c->error_prev_line = token.line;
c->error_prev_column = token.column;
#if 0
gb_printf_err("%s()\n", function);
#endif
va_start(va, fmt);
gb_printf_err("%s(%td:%td) %s\n",
c->parser->tokenizer.fullpath, token.line, token.column,
gb_bprintf_va(fmt, va));
va_end(va);
error:
c->error_count++;
// NOTE(bill): If there are too many errors, just quit
if (c->error_count > MAX_CHECKER_ERROR_COUNT) {
gb_exit(1);
return;
}
}
Entity *entity_of_identifier(Checker *c, AstNode *identifier) {
GB_ASSERT(identifier->kind == AstNode_Identifier);
Entity **found = map_get(&c->definitions, hash_pointer(identifier));
if (found)
return *found;
found = map_get(&c->uses, hash_pointer(identifier));
if (found)
return *found;
return NULL;
}
Type *type_of_expression(Checker *c, AstNode *expression) {
TypeAndValue *found = map_get(&c->types, hash_pointer(expression));
if (found)
return found->type;
if (expression->kind == AstNode_Identifier) {
Entity *entity = entity_of_identifier(c, expression);
if (entity)
return entity->type;
}
return NULL;
}
void add_untyped(Checker *c, AstNode *expression, b32 lhs, AddressingMode mode, Type *basic_type, Value value) {
map_set(&c->untyped, hash_pointer(expression), make_expression_info(lhs, mode, basic_type, value));
}
void add_type_and_value(Checker *c, AstNode *expression, AddressingMode mode, Type *type, Value value) {
GB_ASSERT(expression != NULL);
GB_ASSERT(type != NULL);
if (mode == Addressing_Invalid)
return;
if (mode == Addressing_Constant) {
GB_ASSERT(value.kind != Value_Invalid);
GB_ASSERT(type == &basic_types[Basic_Invalid] || is_type_constant_type(type));
}
TypeAndValue tv = {};
tv.type = type;
tv.value = value;
map_set(&c->types, hash_pointer(expression), tv);
}
void add_entity_definition(Checker *c, AstNode *identifier, Entity *entity) {
GB_ASSERT(identifier != NULL);
GB_ASSERT(identifier->kind == AstNode_Identifier);
u64 key = hash_pointer(identifier);
map_set(&c->definitions, key, entity);
}
void add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
Entity *insert_entity = scope_insert_entity(scope, entity);
if (insert_entity) {
print_checker_error(c, entity->token, "Redeclared entity in this scope: %.*s", LIT(entity->token.string));
return;
}
if (identifier)
add_entity_definition(c, identifier, entity);
}
void add_entity_use(Checker *c, AstNode *identifier, Entity *entity) {
GB_ASSERT(identifier != NULL);
GB_ASSERT(identifier->kind == AstNode_Identifier);
u64 key = hash_pointer(identifier);
map_set(&c->uses, key, entity);
}
void add_scope(Checker *c, AstNode *node, Scope *scope) {
GB_ASSERT(node != NULL);
GB_ASSERT(scope != NULL);
map_set(&c->scopes, hash_pointer(node), scope);
}
void check_open_scope(Checker *c, AstNode *statement) {
Scope *scope = make_scope(c->curr_scope);
add_scope(c, statement, scope);
c->curr_scope = scope;
}
void check_close_scope(Checker *c) {
c->curr_scope = c->curr_scope->parent;
}
void push_procedure(Checker *c, Type *procedure_type) {
gb_array_append(c->procedure_stack, procedure_type);
}
void pop_procedure(Checker *c) {
gb_array_pop(c->procedure_stack);
}
Entity *make_entity_variable(Checker *c, Scope *parent, Token token, Type *type) {
Entity *entity = alloc_entity(gb_arena_allocator(&c->entity_arena), Entity_Variable, parent, token, type);
return entity;
}
Entity *make_entity_constant(Checker *c, Scope *parent, Token token, Type *type, Value value) {
Entity *entity = alloc_entity(gb_arena_allocator(&c->entity_arena), Entity_Constant, parent, token, type);
entity->constant.value = value;
return entity;
}
Entity *make_entity_type_name(Checker *c, Scope *parent, Token token, Type *type) {
Entity *entity = alloc_entity(gb_arena_allocator(&c->entity_arena), Entity_TypeName, parent, token, type);
return entity;
}
Entity *make_entity_param(Checker *c, Scope *parent, Token token, Type *type) {
Entity *entity = alloc_entity(gb_arena_allocator(&c->entity_arena), Entity_Variable, parent, token, type);
entity->variable.used = true;
return entity;
}
Entity *make_entity_field(Checker *c, Scope *parent, Token token, Type *type) {
Entity *entity = alloc_entity(gb_arena_allocator(&c->entity_arena), Entity_Variable, parent, token, type);
entity->variable.is_field = true;
return entity;
}
Entity *make_entity_procedure(Checker *c, Scope *parent, Token token, Type *signature_type) {
Entity *entity = alloc_entity(gb_arena_allocator(&c->entity_arena), Entity_Procedure, parent, token, signature_type);
return entity;
}
Entity *make_entity_builtin(Checker *c, Scope *parent, Token token, Type *type, i32 id) {
Entity *entity = alloc_entity(gb_arena_allocator(&c->entity_arena), Entity_Builtin, parent, token, type);
entity->builtin.id = id;
return entity;
}
Entity *make_entity_dummy_variable(Checker *c, Token token) {
token.string = make_string("_");
return make_entity_variable(c, c->file_scope, token, NULL);
}
+66
View File
@@ -0,0 +1,66 @@
struct Scope;
struct Checker;
enum EntityKind {
Entity_Invalid,
Entity_Constant,
Entity_Variable,
Entity_TypeName,
Entity_Procedure,
Entity_Builtin,
Entity_Count,
};
typedef i64 EntityGuid;
struct Entity {
EntityKind kind;
EntityGuid guid;
Scope *parent;
Token token;
Type *type;
union {
struct { Value value; } constant;
struct {
b8 visited;
b8 is_field;
b8 used;
} variable;
struct {} type_name;
struct {} procedure;
struct { i32 id; } builtin;
};
};
gb_global gbAtomic64 entity_guid_counter = {0};
EntityGuid next_entity_guid(void) {
return cast(EntityGuid)gb_atomic64_fetch_add(&entity_guid_counter, 1);
}
Entity *alloc_entity(gbAllocator a, EntityKind kind, Scope *parent, Token token, Type *type) {
Entity *entity = gb_alloc_item(a, Entity);
entity->kind = kind;
entity->guid = next_entity_guid();
entity->parent = parent;
entity->token = token;
entity->type = type;
return entity;
}
Entity *make_entity_variable (Checker *c, Scope *parent, Token token, Type *type);
Entity *make_entity_constant (Checker *c, Scope *parent, Token token, Type *type, Value value);
Entity *make_entity_type_name(Checker *c, Scope *parent, Token token, Type *type);
Entity *make_entity_param (Checker *c, Scope *parent, Token token, Type *type);
Entity *make_entity_field (Checker *c, Scope *parent, Token token, Type *type);
Entity *make_entity_procedure(Checker *c, Scope *parent, Token token, Type *signature_type);
Entity *make_entity_builtin (Checker *c, Scope *parent, Token token, Type *type, i32 id);
Entity *make_entity_dummy_variable(Checker *c, Token token);
File diff suppressed because it is too large Load Diff
+695
View File
@@ -0,0 +1,695 @@
//
b32 check_assignable_to(Checker *c, Operand *operand, Type *type) {
if (operand->mode == Addressing_Invalid ||
type == &basic_types[Basic_Invalid]) {
return true;
}
Type *s = operand->type;
if (are_types_identical(s, type))
return true;
Type *sb = get_base_type(s);
Type *tb = get_base_type(type);
if (is_type_untyped(sb)) {
switch (tb->kind) {
case Type_Basic:
if (operand->mode == Addressing_Constant)
return check_value_is_expressible(c, operand->value, tb, NULL);
if (sb->kind == Type_Basic)
return sb->basic.kind == Basic_UntypedBool && is_type_boolean(tb);
break;
case Type_Pointer:
return sb->basic.kind == Basic_UntypedPointer;
}
}
if (are_types_identical(sb, tb) && (!is_type_named(sb) || !is_type_named(tb)))
return true;
if (is_type_pointer(sb) && is_type_rawptr(tb))
return true;
if (is_type_rawptr(sb) && is_type_pointer(tb))
return true;
if (sb->kind == Type_Array && tb->kind == Type_Array) {
if (are_types_identical(sb->array.element, tb->array.element)) {
if (tb->array.count == 0) // NOTE(bill): Not static size
return true;
}
}
return false;
}
// NOTE(bill): `content_name` is for debugging
void check_assignment(Checker *c, Operand *operand, Type *type, String context_name) {
check_not_tuple(c, operand);
if (operand->mode == Addressing_Invalid)
return;
if (is_type_untyped(operand->type)) {
Type *target_type = type;
if (type == NULL)
target_type = default_type(operand->type);
convert_to_typed(c, operand, target_type);
if (operand->mode == Addressing_Invalid)
return;
}
if (type != NULL) {
if (!check_assignable_to(c, operand, type)) {
gbString type_string = type_to_string(type);
gbString op_type_string = type_to_string(operand->type);
defer (gb_string_free(type_string));
defer (gb_string_free(op_type_string));
// TODO(bill): is this a good enough error message?
print_checker_error(c, ast_node_token(operand->expression),
"Cannot assign value `%.*s` of type `%s` to `%s` in %.*s",
LIT(ast_node_token(operand->expression).string),
op_type_string,
type_string,
LIT(context_name));
operand->mode = Addressing_Invalid;
}
}
}
Type *check_assign_variable(Checker *c, Operand *x, AstNode *lhs) {
if (x->mode == Addressing_Invalid ||
x->type == &basic_types[Basic_Invalid]) {
return NULL;
}
AstNode *node = unparen_expression(lhs);
// NOTE(bill): Ignore assignments to `_`
if (node->kind == AstNode_Identifier &&
are_strings_equal(node->identifier.token.string, make_string("_"))) {
add_entity_definition(c, node, NULL);
check_assignment(c, x, NULL, make_string("assignment to `_` identifier"));
if (x->mode == Addressing_Invalid)
return NULL;
return x->type;
}
Entity *e = NULL;
b32 used = false;
if (node->kind == AstNode_Identifier) {
scope_lookup_parent_entity(c->curr_scope, node->identifier.token.string,
NULL, &e);
if (e != NULL && e->kind == Entity_Variable) {
used = e->variable.used; // TODO(bill): Make backup just in case
}
}
Operand y = {Addressing_Invalid};
check_expression(c, &y, lhs);
if (e) e->variable.used = used;
if (y.mode == Addressing_Invalid ||
y.type == &basic_types[Basic_Invalid]) {
return NULL;
}
switch (y.mode) {
case Addressing_Variable:
break;
case Addressing_Invalid:
return NULL;
default: {
if (y.expression->kind == AstNode_SelectorExpression) {
// NOTE(bill): Extra error checks
Operand z = {Addressing_Invalid};
check_expression(c, &z, y.expression->selector_expression.operand);
}
gbString str = expression_to_string(y.expression);
defer (gb_string_free(str));
print_checker_error(c, ast_node_token(y.expression),
"Cannot assign to `%s`", str);
} break;
}
check_assignment(c, x, y.type, make_string("assignment"));
if (x->mode == Addressing_Invalid)
return NULL;
return x->type;
}
void check_assign_variables(Checker *c,
AstNode *lhs_list, isize lhs_count,
AstNode *rhs_list, isize rhs_count) {
Operand operand = {Addressing_Invalid};
AstNode *lhs = lhs_list, *rhs = rhs_list;
for (;
lhs != NULL && rhs != NULL;
lhs = lhs->next, rhs = rhs->next) {
check_multi_expression(c, &operand, rhs);
if (operand.type->kind != Type_Tuple) {
check_assign_variable(c, &operand, lhs);
} else {
auto *tuple = &operand.type->tuple;
for (isize i = 0;
i < tuple->variable_count && lhs != NULL;
i++, lhs = lhs->next) {
// TODO(bill): More error checking
operand.type = tuple->variables[i]->type;
check_assign_variable(c, &operand, lhs);
}
if (lhs == NULL)
break;
}
}
}
// NOTE(bill): `content_name` is for debugging
Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) {
if (operand->mode == Addressing_Invalid ||
operand->type == &basic_types[Basic_Invalid] ||
e->type == &basic_types[Basic_Invalid]) {
if (e->type == NULL)
e->type = &basic_types[Basic_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 == &basic_types[Basic_Invalid]) {
print_checker_error(c, e->token, "Use of untyped thing in %.*s", LIT(context_name));
e->type = &basic_types[Basic_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, AstNode *init_list, isize init_count, String context_name) {
if (lhs_count == 0 && init_count == 0)
return;
isize i = 0;
AstNode *rhs = init_list;
for (;
i < lhs_count && rhs != NULL;
i++, rhs = rhs->next) {
Operand operand = {};
check_multi_expression(c, &operand, rhs);
if (operand.type->kind != Type_Tuple) {
check_init_variable(c, lhs[i], &operand, context_name);
} else {
auto *tuple = &operand.type->tuple;
for (isize j = 0;
j < tuple->variable_count && i < lhs_count;
j++, i++) {
Type *type = tuple->variables[j]->type;
operand.type = type;
check_init_variable(c, lhs[i], &operand, context_name);
}
}
}
if (i < lhs_count) {
if (lhs[i]->type == NULL)
print_checker_error(c, lhs[i]->token, "Too few values on the right hand side of the declaration");
} else if (rhs != NULL) {
print_checker_error(c, ast_node_token(rhs), "Too many values on the right hand side of the declaration");
}
}
void check_init_constant(Checker *c, Entity *e, Operand *operand) {
if (operand->mode == Addressing_Invalid ||
operand->type == &basic_types[Basic_Invalid] ||
e->type == &basic_types[Basic_Invalid]) {
if (e->type == NULL)
e->type = &basic_types[Basic_Invalid];
return;
}
if (operand->mode != Addressing_Constant) {
// TODO(bill): better error
print_checker_error(c, ast_node_token(operand->expression),
"`%.*s` is not a constant", LIT(ast_node_token(operand->expression).string));
if (e->type == NULL)
e->type = &basic_types[Basic_Invalid];
return;
}
if (!is_type_constant_type(operand->type)) {
// NOTE(bill): no need to free string as it's panicking
GB_PANIC("Type `%s` not constant!!!", type_to_string(operand->type));
}
if (e->type == NULL) // NOTE(bill): type inference
e->type = operand->type;
check_assignment(c, operand, e->type, make_string("constant declaration"));
if (operand->mode == Addressing_Invalid)
return;
e->constant.value = operand->value;
}
void check_constant_declaration(Checker *c, Entity *e, AstNode *type_expression, AstNode *init_expression) {
GB_ASSERT(e->type == NULL);
if (e->variable.visited) {
e->type = &basic_types[Basic_Invalid];
return;
}
e->variable.visited = true;
if (type_expression) {
Type *t = check_type(c, type_expression);
if (!is_type_constant_type(t)) {
gbString str = type_to_string(t);
defer (gb_string_free(str));
print_checker_error(c, ast_node_token(type_expression),
"Invalid constant type `%s`", str);
e->type = &basic_types[Basic_Invalid];
return;
}
e->type = t;
}
Operand operand = {Addressing_Invalid};
if (init_expression)
check_expression(c, &operand, init_expression);
check_init_constant(c, e, &operand);
}
void check_statement(Checker *c, AstNode *node);
void check_statement_list(Checker *c, AstNode *node) {
for (; node != NULL; node = node->next)
check_statement(c, node);
}
b32 check_is_terminating(Checker *c, AstNode *node);
b32 check_is_terminating_list(Checker *c, AstNode *node_list) {
AstNode *end_of_list = node_list;
for (; end_of_list != NULL; end_of_list = end_of_list->next) {
if (end_of_list->next == NULL)
break;
}
for (AstNode *node = end_of_list; node != NULL; node = node->prev) {
if (node->kind == AstNode_EmptyStatement)
continue;
return check_is_terminating(c, node);
}
return false;
}
b32 check_is_terminating(Checker *c, AstNode *node) {
switch (node->kind) {
case AstNode_BlockStatement:
return check_is_terminating_list(c, node->block_statement.list);
case AstNode_ExpressionStatement:
return check_is_terminating(c, node->expression_statement.expression);
case AstNode_ReturnStatement:
return true;
case AstNode_IfStatement:
if (node->if_statement.else_statement != NULL) {
if (check_is_terminating(c, node->if_statement.body) &&
check_is_terminating(c, node->if_statement.else_statement))
return true;
}
break;
case AstNode_ForStatement:
if (node->for_statement.cond == NULL) {
return true;
}
break;
}
return false;
}
void check_statement(Checker *c, AstNode *node) {
switch (node->kind) {
case AstNode_EmptyStatement: break;
case AstNode_BadStatement: break;
case AstNode_BadDeclaration: break;
case AstNode_ExpressionStatement: {
Operand operand = {Addressing_Invalid};
ExpressionKind kind = check_expression_base(c, &operand, node->expression_statement.expression);
switch (operand.mode) {
case Addressing_Type:
print_checker_error(c, ast_node_token(node), "Is not an expression");
break;
default:
if (kind == Expression_Statement)
return;
print_checker_error(c, ast_node_token(node), "Expression is not used");
break;
}
} break;
case AstNode_IncDecStatement: {
Token op = {};
auto *s = &node->inc_dec_statement;
op = s->op;
switch (s->op.kind) {
case Token_Increment:
op.kind = Token_Add;
op.string.len = 1;
break;
case Token_Decrement:
op.kind = Token_Sub;
op.string.len = 1;
break;
default:
print_checker_error(c, s->op, "Unknown inc/dec operation %.*s", LIT(s->op.string));
return;
}
Operand operand = {Addressing_Invalid};
check_expression(c, &operand, s->expression);
if (operand.mode == Addressing_Invalid)
return;
if (!is_type_numeric(operand.type)) {
print_checker_error(c, s->op, "Non numeric type");
return;
}
AstNode basic_lit = {AstNode_BasicLiteral};
basic_lit.basic_literal = s->op;
basic_lit.basic_literal.kind = Token_Integer;
basic_lit.basic_literal.string = make_string("1");
AstNode be = {AstNode_BinaryExpression};
be.binary_expression.op = op;
be.binary_expression.left = s->expression;;
be.binary_expression.right = &basic_lit;
check_binary_expression(c, &operand, &be);
} break;
case AstNode_AssignStatement:
switch (node->assign_statement.op.kind) {
case Token_Eq:
if (node->assign_statement.lhs_count == 0) {
print_checker_error(c, node->assign_statement.op, "Missing lhs in assignment statement");
return;
}
check_assign_variables(c,
node->assign_statement.lhs_list, node->assign_statement.lhs_count,
node->assign_statement.rhs_list, node->assign_statement.rhs_count);
break;
default: {
Token op = node->assign_statement.op;
if (node->assign_statement.lhs_count != 1 ||
node->assign_statement.rhs_count != 1) {
print_checker_error(c, op,
"assignment operation `%.*s` requires single-valued expressions", LIT(op.string));
return;
}
// TODO(bill): Check if valid assignment operator
Operand operand = {Addressing_Invalid};
AstNode be = {AstNode_BinaryExpression};
be.binary_expression.op = op;
// NOTE(bill): Only use the first one will be used
be.binary_expression.left = node->assign_statement.lhs_list;
be.binary_expression.right = node->assign_statement.rhs_list;
check_binary_expression(c, &operand, &be);
if (operand.mode == Addressing_Invalid)
return;
// NOTE(bill): Only use the first one will be used
check_assign_variable(c, &operand, node->assign_statement.lhs_list);
} break;
}
break;
case AstNode_BlockStatement:
check_open_scope(c, node);
check_statement_list(c, node->block_statement.list);
check_close_scope(c);
break;
case AstNode_IfStatement: {
Operand operand = {Addressing_Invalid};
check_expression(c, &operand, node->if_statement.cond);
if (operand.mode != Addressing_Invalid &&
!is_type_boolean(operand.type)) {
print_checker_error(c, ast_node_token(node->if_statement.cond),
"Non-boolean condition in `if` statement");
}
check_statement(c, node->if_statement.body);
if (node->if_statement.else_statement) {
switch (node->if_statement.else_statement->kind) {
case AstNode_IfStatement:
case AstNode_BlockStatement:
check_statement(c, node->if_statement.else_statement);
break;
default:
print_checker_error(c, ast_node_token(node->if_statement.else_statement),
"Invalid `else` statement in `if` statement");
break;
}
}
} break;
case AstNode_ReturnStatement: {
auto *rs = &node->return_statement;
GB_ASSERT(gb_array_count(c->procedure_stack) > 0);
if (c->in_defer) {
print_checker_error(c, rs->token, "You cannot `return` within a defer statement");
// TODO(bill): Should I break here?
break;
}
Type *proc_type = c->procedure_stack[gb_array_count(c->procedure_stack)-1];
isize result_count = 0;
if (proc_type->procedure.results)
result_count = proc_type->procedure.results->tuple.variable_count;
if (result_count != rs->result_count) {
print_checker_error(c, rs->token, "Expected %td return %s, got %td",
result_count,
(result_count != 1 ? "values" : "value"),
rs->result_count);
} else if (result_count > 0) {
auto *tuple = &proc_type->procedure.results->tuple;
check_init_variables(c, tuple->variables, tuple->variable_count,
rs->results, rs->result_count, make_string("return statement"));
}
} break;
case AstNode_ForStatement: {
check_open_scope(c, node);
defer (check_close_scope(c));
check_statement(c, node->for_statement.init);
if (node->for_statement.cond) {
Operand operand = {Addressing_Invalid};
check_expression(c, &operand, node->for_statement.cond);
if (operand.mode != Addressing_Invalid &&
!is_type_boolean(operand.type)) {
print_checker_error(c, ast_node_token(node->for_statement.cond),
"Non-boolean condition in `for` statement");
}
}
check_statement(c, node->for_statement.end);
check_statement(c, node->for_statement.body);
} break;
case AstNode_DeferStatement: {
auto *ds = &node->defer_statement;
if (is_ast_node_declaration(ds->statement)) {
print_checker_error(c, ds->token, "You cannot defer a declaration");
} else {
b32 out_in_defer = c->in_defer;
c->in_defer = true;
check_statement(c, ds->statement);
c->in_defer = out_in_defer;
}
} break;
// Declarations
case AstNode_VariableDeclaration: {
auto *vd = &node->variable_declaration;
gbAllocator allocator = gb_arena_allocator(&c->entity_arena);
isize entity_count = vd->name_list_count;
isize entity_index = 0;
Entity **entities = gb_alloc_array(allocator, Entity *, entity_count);
switch (vd->kind) {
case Declaration_Mutable: {
Entity **new_entities = gb_alloc_array(allocator, Entity *, entity_count);
isize new_entity_count = 0;
for (AstNode *name = vd->name_list; name != NULL; name = name->next) {
Entity *entity = NULL;
Token token = name->identifier.token;
if (name->kind == AstNode_Identifier) {
String str = token.string;
Entity *found = NULL;
// NOTE(bill): Ignore assignments to `_`
b32 can_be_ignored = are_strings_equal(str, make_string("_"));
if (!can_be_ignored) {
found = scope_lookup_entity_current(c->curr_scope, str);
}
if (found == NULL) {
entity = make_entity_variable(c, c->curr_scope, token, NULL);
if (!can_be_ignored) {
new_entities[new_entity_count++] = entity;
}
add_entity_definition(c, name, entity);
} else {
entity = found;
}
} else {
print_checker_error(c, token, "A variable declaration must be an identifier");
}
if (entity == NULL)
entity = make_entity_dummy_variable(c, token);
entities[entity_index++] = entity;
}
Type *init_type = NULL;
if (vd->type_expression) {
init_type = check_type(c, vd->type_expression, NULL);
if (init_type == NULL)
init_type = &basic_types[Basic_Invalid];
}
for (isize i = 0; i < entity_count; i++) {
Entity *e = entities[i];
GB_ASSERT(e != NULL);
if (e->variable.visited) {
e->type = &basic_types[Basic_Invalid];
continue;
}
e->variable.visited = true;
if (e->type == NULL)
e->type = init_type;
}
check_init_variables(c, entities, entity_count, vd->value_list, vd->value_list_count, make_string("variable declaration"));
AstNode *name = vd->name_list;
for (isize i = 0; i < new_entity_count; i++, name = name->next) {
add_entity(c, c->curr_scope, name, new_entities[i]);
}
} break;
case Declaration_Immutable: {
for (AstNode *name = vd->name_list, *value = vd->value_list;
name != NULL && value != NULL;
name = name->next, value = value->next) {
GB_ASSERT(name->kind == AstNode_Identifier);
Value v = {Value_Invalid};
Entity *e = make_entity_constant(c, c->curr_scope, name->identifier.token, NULL, v);
entities[entity_index++] = e;
check_constant_declaration(c, e, vd->type_expression, value);
}
isize lhs_count = vd->name_list_count;
isize rhs_count = vd->value_list_count;
// TODO(bill): Better error messages or is this good enough?
if (rhs_count == 0 && vd->type_expression == NULL) {
print_checker_error(c, ast_node_token(node), "Missing type or initial expression");
} else if (lhs_count < rhs_count) {
print_checker_error(c, ast_node_token(node), "Extra initial expression");
}
AstNode *name = vd->name_list;
for (isize i = 0; i < entity_count; i++, name = name->next) {
add_entity(c, c->curr_scope, name, entities[i]);
}
} break;
default:
print_checker_error(c, ast_node_token(node), "Unknown variable declaration kind. Probably an invalid AST.");
return;
}
} break;
case AstNode_ProcedureDeclaration: {
auto *pd = &node->procedure_declaration;
GB_ASSERT_MSG(pd->kind == Declaration_Immutable, "Mutable/temp procedures are not yet implemented");
// TODO(bill): Should this be the case? And are the scopes correct?
// TODO(bill): Should procedures just have global scope?
Entity *e = make_entity_procedure(c, c->curr_scope, pd->name->identifier.token, NULL);
add_entity(c, c->curr_scope, pd->name, e);
Type *proc_type = make_type_procedure(e->parent, NULL, 0, NULL, 0);
e->type = proc_type;
check_open_scope(c, pd->procedure_type);
{
check_procedure_type(c, proc_type, pd->procedure_type);
if (pd->body) {
GB_ASSERT(pd->body->kind == AstNode_BlockStatement);
push_procedure(c, proc_type);
check_statement_list(c, pd->body->block_statement.list);
if (pd->procedure_type->procedure_type.result_count > 0) {
if (!check_is_terminating(c, pd->body)) {
print_checker_error(c, pd->body->block_statement.close, "Missing return statement at the end of the procedure");
}
}
pop_procedure(c);
} else if (pd->tag) {
GB_ASSERT(pd->tag->kind == AstNode_TagExpression);
String tag_name = pd->tag->tag_expression.name.string;
if (gb_strncmp("foreign", cast(char *)tag_name.text, tag_name.len) == 0) {
// NOTE(bill): Foreign procedure (linking stage)
}
}
}
check_close_scope(c);
} break;
case AstNode_TypeDeclaration: {
auto *td = &node->type_declaration;
AstNode *name = td->name;
Entity *e = make_entity_type_name(c, c->curr_scope, name->identifier.token, NULL);
add_entity(c, c->curr_scope, name, e);
e->type = make_type_named(e->token.string, NULL, e);
check_type(c, td->type_expression, e->type);
// NOTE(bill): Prevent recursive definition
set_base_type(e->type, get_base_type(e->type));
} break;
}
}
+576
View File
@@ -0,0 +1,576 @@
struct Scope;
enum BasicKind {
Basic_Invalid,
Basic_bool,
Basic_i8,
Basic_i16,
Basic_i32,
Basic_i64,
Basic_u8,
Basic_u16,
Basic_u32,
Basic_u64,
Basic_f32,
Basic_f64,
Basic_int,
Basic_uint,
Basic_rawptr,
Basic_string,
Basic_UntypedBool,
Basic_UntypedInteger,
Basic_UntypedFloat,
Basic_UntypedPointer,
Basic_UntypedString,
Basic_UntypedRune,
Basic_Count,
Basic_rune = Basic_i32,
};
enum BasicFlag : u32 {
BasicFlag_Boolean = GB_BIT(0),
BasicFlag_Integer = GB_BIT(1),
BasicFlag_Unsigned = GB_BIT(2),
BasicFlag_Float = GB_BIT(3),
BasicFlag_Pointer = GB_BIT(4),
BasicFlag_String = GB_BIT(5),
BasicFlag_Untyped = GB_BIT(6),
BasicFlag_Numeric = BasicFlag_Integer | BasicFlag_Float,
BasicFlag_Ordered = BasicFlag_Numeric | BasicFlag_String | BasicFlag_Pointer,
BasicFlag_ConstantType = BasicFlag_Boolean | BasicFlag_Numeric | BasicFlag_String | BasicFlag_Pointer,
};
struct BasicType {
BasicKind kind;
u32 flags;
String name;
};
enum TypeKind {
Type_Invalid,
Type_Basic,
Type_Array,
Type_Structure,
Type_Pointer,
Type_Named,
Type_Tuple,
Type_Procedure,
Type_Count,
};
struct Type {
TypeKind kind;
union {
BasicType basic;
struct {
Type *element;
i64 count;
} array;
struct {
// Theses are arrays
Entity **fields; // Entity_Variable
isize field_count; // == offset_count
i64 * offsets;
b32 offsets_set;
} structure;
struct { Type *element; } pointer;
struct {
String name;
Type * base;
Entity *type_name; // Entity_TypeName
} named;
struct {
Entity **variables; // Entity_Variable
isize variable_count;
} tuple;
struct {
Scope *scope;
Type * params; // Type_Tuple
Type * results; // Type_Tuple
isize params_count;
isize results_count;
} procedure;
};
};
Type *get_base_type(Type *t) {
while (t->kind == Type_Named)
t = t->named.base;
return t;
}
void set_base_type(Type *t, Type *base) {
if (t && t->kind == Type_Named) {
t->named.base = base;
}
}
Type *alloc_type(TypeKind kind) {
Type *t = gb_alloc_item(gb_heap_allocator(), Type);
t->kind = kind;
return t;
}
Type *make_type_basic(BasicType basic) {
Type *t = alloc_type(Type_Basic);
t->basic = basic;
return t;
}
Type *make_type_array(Type *element, i64 count) {
Type *t = alloc_type(Type_Array);
t->array.element = element;
t->array.count = count;
return t;
}
Type *make_type_structure(void) {
Type *t = alloc_type(Type_Structure);
return t;
}
Type *make_type_pointer(Type *element) {
Type *t = alloc_type(Type_Pointer);
t->pointer.element = element;
return t;
}
Type *make_type_named(String name, Type *base, Entity *type_name) {
Type *t = alloc_type(Type_Named);
t->named.name = name;
t->named.base = base;
t->named.type_name = type_name;
return t;
}
Type *make_type_tuple(void) {
Type *t = alloc_type(Type_Tuple);
return t;
}
Type *make_type_procedure(Scope *scope, Type *params, isize params_count, Type *results, isize results_count) {
Type *t = alloc_type(Type_Procedure);
t->procedure.scope = scope;
t->procedure.params = params;
t->procedure.params_count = params_count;
t->procedure.results = results;
t->procedure.results_count = results_count;
return t;
}
#define STR_LIT(x) {cast(u8 *)x, gb_size_of(x)-1}
gb_global Type basic_types[] = {
{Type_Basic, {Basic_Invalid, 0, STR_LIT("invalid type")}},
{Type_Basic, {Basic_bool, BasicFlag_Boolean, STR_LIT("bool")}},
{Type_Basic, {Basic_i8, BasicFlag_Integer, STR_LIT("i8")}},
{Type_Basic, {Basic_i16, BasicFlag_Integer, STR_LIT("i16")}},
{Type_Basic, {Basic_i32, BasicFlag_Integer, STR_LIT("i32")}},
{Type_Basic, {Basic_i64, BasicFlag_Integer, STR_LIT("i64")}},
{Type_Basic, {Basic_u8, BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u8")}},
{Type_Basic, {Basic_u16, BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u16")}},
{Type_Basic, {Basic_u32, BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u32")}},
{Type_Basic, {Basic_u64, BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("u64")}},
{Type_Basic, {Basic_f32, BasicFlag_Float, STR_LIT("f32")}},
{Type_Basic, {Basic_f64, BasicFlag_Float, STR_LIT("f64")}},
{Type_Basic, {Basic_int, BasicFlag_Integer, STR_LIT("int")}},
{Type_Basic, {Basic_uint, BasicFlag_Integer | BasicFlag_Unsigned, STR_LIT("uint")}},
{Type_Basic, {Basic_rawptr, BasicFlag_Pointer, STR_LIT("rawptr")}},
{Type_Basic, {Basic_string, BasicFlag_String, STR_LIT("string")}},
{Type_Basic, {Basic_UntypedBool, BasicFlag_Boolean | BasicFlag_Untyped, STR_LIT("untyped bool")}},
{Type_Basic, {Basic_UntypedInteger, BasicFlag_Integer | BasicFlag_Untyped, STR_LIT("untyped integer")}},
{Type_Basic, {Basic_UntypedFloat, BasicFlag_Float | BasicFlag_Untyped, STR_LIT("untyped float")}},
{Type_Basic, {Basic_UntypedPointer, BasicFlag_Pointer | BasicFlag_Untyped, STR_LIT("untyped pointer")}},
{Type_Basic, {Basic_UntypedString, BasicFlag_String | BasicFlag_Untyped, STR_LIT("untyped string")}},
{Type_Basic, {Basic_UntypedRune, BasicFlag_Integer | BasicFlag_Untyped, STR_LIT("untyped rune")}},
};
gb_global Type basic_type_aliases[] = {
{Type_Basic, {Basic_rune, BasicFlag_Integer, STR_LIT("rune")}},
};
b32 is_type_named(Type *t) {
if (t->kind == Type_Basic)
return true;
return t->kind == Type_Named;
}
b32 is_type_boolean(Type *t) {
if (t->kind == Type_Basic)
return (t->basic.flags & BasicFlag_Boolean) != 0;
return false;
}
b32 is_type_integer(Type *t) {
if (t->kind == Type_Basic)
return (t->basic.flags & BasicFlag_Integer) != 0;
return false;
}
b32 is_type_unsigned(Type *t) {
if (t->kind == Type_Basic)
return (t->basic.flags & BasicFlag_Unsigned) != 0;
return false;
}
b32 is_type_numeric(Type *t) {
if (t->kind == Type_Basic)
return (t->basic.flags & BasicFlag_Numeric) != 0;
return false;
}
b32 is_type_string(Type *t) {
if (t->kind == Type_Basic)
return (t->basic.flags & BasicFlag_String) != 0;
return false;
}
b32 is_type_typed(Type *t) {
if (t->kind == Type_Basic)
return (t->basic.flags & BasicFlag_Untyped) == 0;
return true;
}
b32 is_type_untyped(Type *t) {
if (t->kind == Type_Basic)
return (t->basic.flags & BasicFlag_Untyped) != 0;
return false;
}
b32 is_type_ordered(Type *t) {
if (t->kind == Type_Basic)
return (t->basic.flags & BasicFlag_Ordered) != 0;
return false;
}
b32 is_type_constant_type(Type *t) {
if (t->kind == Type_Basic)
return (t->basic.flags & BasicFlag_ConstantType) != 0;
return false;
}
b32 is_type_float(Type *t) {
if (t->kind == Type_Basic)
return (t->basic.flags & BasicFlag_Float) != 0;
return false;
}
b32 is_type_pointer(Type *t) {
if (t->kind == Type_Basic)
return (t->basic.flags & BasicFlag_Pointer) != 0;
return t->kind == Type_Pointer;
}
b32 is_type_int_or_uint(Type *t) {
if (t->kind == Type_Basic)
return (t->basic.kind == Basic_int) || (t->basic.kind == Basic_uint);
return false;
}
b32 is_type_rawptr(Type *t) {
if (t->kind == Type_Basic)
return t->basic.kind == Basic_rawptr;
return false;
}
b32 is_type_comparable(Type *t) {
t = get_base_type(t);
switch (t->kind) {
case Type_Basic:
return true;
case Type_Pointer:
return true;
case Type_Structure: {
for (isize i = 0; i < t->structure.field_count; i++) {
if (!is_type_comparable(t->structure.fields[i]->type))
return false;
}
return true;
} break;
case Type_Array:
return is_type_comparable(t->array.element);
}
return false;
}
b32 are_types_identical(Type *x, Type *y) {
if (x == y)
return true;
switch (x->kind) {
case Type_Basic:
if (y->kind == Type_Basic)
return x->basic.kind == y->basic.kind;
break;
case Type_Array:
if (y->kind == Type_Array)
return (x->array.count == y->array.count) && are_types_identical(x->array.element, y->array.element);
break;
case Type_Structure:
if (y->kind == Type_Structure) {
if (x->structure.field_count == y->structure.field_count) {
for (isize i = 0; i < x->structure.field_count; i++) {
if (!are_types_identical(x->structure.fields[i]->type, y->structure.fields[i]->type)) {
return false;
}
}
return true;
}
}
break;
case Type_Pointer:
if (y->kind == Type_Pointer)
return are_types_identical(x->pointer.element, y->pointer.element);
break;
case Type_Named:
if (y->kind == Type_Named)
return x->named.base == y->named.base;
break;
case Type_Tuple:
if (y->kind == Type_Tuple) {
if (x->tuple.variable_count == y->tuple.variable_count) {
for (isize i = 0; i < x->tuple.variable_count; i++) {
if (!are_types_identical(x->tuple.variables[i]->type, y->tuple.variables[i]->type))
return false;
}
return true;
}
}
break;
case Type_Procedure:
if (y->kind == Type_Procedure) {
return are_types_identical(x->procedure.params, y->procedure.params) &&
are_types_identical(x->procedure.results, y->procedure.results);
}
break;
}
return false;
}
Type *default_type(Type *type) {
if (type->kind == Type_Basic) {
switch (type->basic.kind) {
case Basic_UntypedBool: return &basic_types[Basic_bool];
case Basic_UntypedInteger: return &basic_types[Basic_int];
case Basic_UntypedFloat: return &basic_types[Basic_f64];
case Basic_UntypedString: return &basic_types[Basic_string];
case Basic_UntypedRune: return &basic_types[Basic_rune];
case Basic_UntypedPointer: return &basic_types[Basic_rawptr];
}
}
return type;
}
struct BaseTypeSizes {
i64 word_size;
i64 max_align;
};
// TODO(bill): Change
gb_global i64 basic_type_sizes[] = {
0, // Basic_Invalid
1, // Basic_bool // TODO(bill): What size should this be? And should I have different booleans?
1, // Basic_i8
2, // Basic_i16
4, // Basic_i32
8, // Basic_i64
1, // Basic_u8
2, // Basic_u16
4, // Basic_u32
8, // Basic_u64
4, // Basic_f32
8, // Basic_f64
};
i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t);
i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t);
i64 align_formula(i64 size, i64 align) {
i64 result = size + align-1;
return result - result%align;
}
i64 type_align_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
t = get_base_type(t);
switch (t->kind) {
case Type_Array:
return type_align_of(s, allocator, t->array.element);
case Type_Structure: {
i64 max = 1;
for (isize i = 0; i < t->structure.field_count; i++) {
i64 align = type_align_of(s, allocator, t->structure.fields[i]->type);
if (max < align)
max = align;
}
return max;
} break;
}
return gb_clamp(type_size_of(s, allocator, t), 1, s.max_align);
}
i64 *type_set_offsets_of(BaseTypeSizes s, gbAllocator allocator, Entity **fields, isize field_count) {
// TODO(bill): use arena allocation
i64 *offsets = gb_alloc_array(allocator, i64, field_count);
i64 curr_offset = 0;
for (isize i = 0; i < field_count; i++) {
i64 align = type_align_of(s, allocator, fields[i]->type);
curr_offset = align_formula(curr_offset, align);
offsets[i] = curr_offset;
curr_offset += type_size_of(s, allocator, fields[i]->type);
}
return offsets;
}
b32 type_set_offsets(BaseTypeSizes s, gbAllocator allocator, Type *t) {
GB_ASSERT(t->kind == Type_Structure);
if (!t->structure.offsets_set) {
t->structure.offsets = type_set_offsets_of(s, allocator, t->structure.fields, t->structure.field_count);
t->structure.offsets_set = true;
return true;
}
return false;
}
i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
t = get_base_type(t);
switch (t->kind) {
case Type_Basic: {
GB_ASSERT(is_type_typed(t));
BasicKind kind = t->basic.kind;
if (kind < gb_count_of(basic_type_sizes)) {
i64 size = basic_type_sizes[kind];
if (size > 0)
return size;
}
if (kind == Basic_string)
return 2 * s.word_size;
} break;
case Type_Array: {
i64 count = t->array.count;
if (count == 0)
return 0;
i64 align = type_align_of(s, allocator, t->array.element);
i64 size = type_size_of(s, allocator, t->array.element);
i64 alignment = align_formula(size, align);
return alignment*(count-1) + size;
} break;
case Type_Structure: {
i64 count = t->structure.field_count;
if (count == 0)
return 0;
type_set_offsets(s, allocator, t);
return t->structure.offsets[count-1] + type_size_of(s, allocator, t->structure.fields[count-1]->type);
} break;
}
// Catch all
return s.word_size;
}
i64 type_offset_of(BaseTypeSizes s, gbAllocator allocator, Type *t, isize index) {
GB_ASSERT(t->kind == Type_Structure);
type_set_offsets(s, allocator, t);
if (gb_is_between(index, 0, t->structure.field_count-1)) {
return t->structure.offsets[index];
}
return 0;
}
gbString write_type_to_string(gbString str, Type *type) {
if (type == NULL) {
return gb_string_appendc(str, "<no type>");
}
switch (type->kind) {
case Type_Basic:
str = gb_string_append_length(str, type->basic.name.text, type->basic.name.len);
break;
case Type_Array:
if (type->array.count >= 0) {
str = gb_string_appendc(str, gb_bprintf("[%td]", type->array.count));
} else {
str = gb_string_appendc(str, "[]");
}
str = write_type_to_string(str, type->array.element);
break;
case Type_Structure: {
str = gb_string_appendc(str, "struct{");
for (isize i = 0; i < type->structure.field_count; i++) {
Entity *f = type->structure.fields[i];
GB_ASSERT(f->kind == Entity_Variable);
if (i < type->structure.field_count-1)
str = gb_string_appendc(str, "; ");
str = gb_string_append_length(str, f->token.string.text, f->token.string.len);
str = gb_string_appendc(str, ": ");
str = write_type_to_string(str, f->type);
}
str = gb_string_appendc(str, "}");
} break;
case Type_Pointer:
str = gb_string_appendc(str, "^");
str = write_type_to_string(str, type->pointer.element);
break;
case Type_Named:
if (type->named.type_name != NULL) {
str = gb_string_append_length(str, type->named.name.text, type->named.name.len);
} else {
// NOTE(bill): Just in case
str = gb_string_appendc(str, "<Type_Named>");
}
break;
case Type_Tuple:
if (type->tuple.variable_count > 0) {
for (isize i = 0; i < type->tuple.variable_count; i++) {
Entity *var = type->tuple.variables[i];
GB_ASSERT(var->kind == Entity_Variable);
if (i > 0)
str = gb_string_appendc(str, ", ");
if (var->token.string.len > 0) {
str = gb_string_append_length(str, var->token.string.text, var->token.string.len);
str = gb_string_appendc(str, ": ");
}
str = write_type_to_string(str, var->type);
}
}
break;
case Type_Procedure:
str = gb_string_appendc(str, "proc(");
if (type->procedure.params)
str = write_type_to_string(str, type->procedure.params);
str = gb_string_appendc(str, ")");
if (type->procedure.results) {
str = gb_string_appendc(str, " -> ");
str = write_type_to_string(str, type->procedure.results);
}
break;
}
return str;
}
gbString type_to_string(Type *type, gbAllocator a = gb_heap_allocator()) {
gbString str = gb_string_make(a, "");
return write_type_to_string(str, type);
}
+365
View File
@@ -0,0 +1,365 @@
#include <math.h>
// TODO(bill): Big numbers
// IMPORTANT TODO(bill): This needs to be completely fixed!!!!!!!!
enum ValueKind {
Value_Invalid,
Value_Bool,
Value_String,
Value_Integer,
Value_Float,
Value_Pointer, // TODO(bill): Value_Pointer
Value_Count,
};
struct Value {
ValueKind kind;
union {
b32 value_bool;
String value_string;
i64 value_integer;
f64 value_float;
void * value_pointer;
};
};
Value make_value_bool(b32 b) {
Value result = {Value_Bool};
result.value_bool = (b != 0);
return result;
}
Value make_value_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
Value result = {Value_String};
result.value_string = string;
return result;
}
Value make_value_integer(String string) {
// TODO(bill): Allow for numbers with underscores in them
Value result = {Value_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;
}
Value make_value_integer(i64 i) {
Value result = {Value_Integer};
result.value_integer = i;
return result;
}
Value make_value_float(String string) {
// TODO(bill): Allow for numbers with underscores in them
Value result = {Value_Float};
result.value_float = gb_str_to_f64(cast(char *)string.text, NULL);
return result;
}
Value make_value_float(f64 f) {
Value result = {Value_Float};
result.value_float = f;
return result;
}
Value make_value_pointer(void *ptr) {
Value result = {Value_Pointer};
result.value_pointer = ptr;
return result;
}
Value make_value_from_basic_literal(Token token) {
switch (token.kind) {
case Token_String: return make_value_string(token.string);
case Token_Integer: return make_value_integer(token.string);
case Token_Float: return make_value_float(token.string);
case Token_Rune: return make_value_integer(token.string);
default:
GB_PANIC("Invalid token for basic literal");
break;
}
Value result = {Value_Invalid};
return result;
}
Value value_to_integer(Value v) {
switch (v.kind) {
case Value_Integer:
return v;
case Value_Float:
return make_value_integer(cast(i64)v.value_float);
}
Value r = {Value_Invalid};
return r;
}
Value value_to_float(Value v) {
switch (v.kind) {
case Value_Integer:
return make_value_float(cast(i64)v.value_integer);
case Value_Float:
return v;
}
Value r = {Value_Invalid};
return r;
}
Value unary_operator_value(Token op, Value v, i32 precision) {
switch (op.kind) {
case Token_Add: {
switch (v.kind) {
case Value_Invalid:
case Value_Integer:
case Value_Float:
return v;
}
} break;
case Token_Sub: {
switch (v.kind) {
case Value_Invalid:
return v;
case Value_Integer: {
Value i = v;
i.value_integer = -i.value_integer;
return i;
}
case Value_Float: {
Value i = v;
i.value_float = -i.value_float;
return i;
}
}
} break;
case Token_Xor: {
i64 i = 0;
switch (v.kind) {
case Value_Invalid:
return v;
case 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 &= ~((-1)<<precision);
return make_value_integer(i);
} break;
case Token_Not: {
switch (v.kind) {
case Value_Invalid: return v;
case Value_Bool:
return make_value_bool(!v.value_bool);
}
} break;
}
failure:
GB_PANIC("Invalid unary operation, %s", token_kind_to_string(op.kind));
Value error_value = {};
return error_value;
}
// NOTE(bill): Make sure things are evaluated in correct order
i32 value_order(Value v) {
switch (v.kind) {
case Value_Invalid:
return 0;
case Value_Bool:
case Value_String:
return 1;
case Value_Integer:
return 2;
case Value_Float:
return 3;
case Value_Pointer:
return 4;
default:
GB_PANIC("How'd you get here? Invalid Value.kind");
return -1;
}
}
void match_values(Value *x, Value *y) {
if (value_order(*y) < value_order(*x)) {
match_values(y, x);
return;
}
switch (x->kind) {
case Value_Invalid:
*y = *x;
return;
case Value_Bool:
case Value_String:
return;
case Value_Integer: {
switch (y->kind) {
case Value_Integer:
return;
case Value_Float:
// TODO(bill): Is this good enough?
*x = make_value_float(cast(f64)x->value_integer);
return;
}
} break;
case Value_Float: {
if (y->kind == Value_Float)
return;
} break;
}
GB_PANIC("How'd you get here? Invalid Value.kind");
}
Value binary_operator_value(Token op, Value x, Value y) {
match_values(&x, &y);
switch (x.kind) {
case Value_Invalid:
return x;
case Value_Bool:
switch (op.kind) {
case Token_CmpAnd: return make_value_bool(x.value_bool && y.value_bool);
case Token_CmpOr: return make_value_bool(x.value_bool || y.value_bool);
default: goto error;
}
break;
case Value_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_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;
default: goto error;
}
return make_value_integer(c);
} break;
case Value_Float: {
f64 a = x.value_float;
f64 b = y.value_float;
switch (op.kind) {
case Token_Add: return make_value_float(a + b);
case Token_Sub: return make_value_float(a - b);
case Token_Mul: return make_value_float(a * b);
case Token_Quo: return make_value_float(a / b);
default: goto error;
}
} break;
}
error:
Value error_value = {};
GB_PANIC("Invalid binary operation: %s", token_kind_to_string(op.kind));
return error_value;
}
gb_inline Value value_add(Value x, Value y) { Token op = {Token_Add}; return binary_operator_value(op, x, y); }
gb_inline Value value_sub(Value x, Value y) { Token op = {Token_Sub}; return binary_operator_value(op, x, y); }
gb_inline Value value_mul(Value x, Value y) { Token op = {Token_Mul}; return binary_operator_value(op, x, y); }
gb_inline Value value_quo(Value x, Value y) { Token op = {Token_Quo}; return binary_operator_value(op, x, y); }
i32 cmp_f64(f64 a, f64 b) {
return (a > b) - (a < b);
}
b32 compare_values(Token op, Value x, Value y) {
match_values(&x, &y);
switch (x.kind) {
case Value_Invalid:
return false;
case Value_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 Value_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 Value_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 Value_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;
}