aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGinger Bill <bill@gingerbill.org>2016-08-30 18:39:29 +0100
committerGinger Bill <bill@gingerbill.org>2016-08-30 18:39:29 +0100
commita06f70d5d95bb7889bf9e8b920d70fd10daf7c12 (patch)
tree59e9577fa42612de96849ee4ce6d8bbd2b38c9b6 /src
parent0eaf7bd830dcda6e00f80eefed36bdf7beb02d5d (diff)
Better `using`; foreign system libraries; optional semicolons
Diffstat (limited to 'src')
-rw-r--r--src/checker/checker.cpp10
-rw-r--r--src/checker/entity.cpp16
-rw-r--r--src/checker/expr.cpp28
-rw-r--r--src/checker/stmt.cpp308
-rw-r--r--src/checker/type.cpp6
-rw-r--r--src/codegen/ssa.cpp37
-rw-r--r--src/main.cpp16
-rw-r--r--src/parser.cpp93
8 files changed, 376 insertions, 138 deletions
diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp
index 717c70b57..d239d0995 100644
--- a/src/checker/checker.cpp
+++ b/src/checker/checker.cpp
@@ -530,7 +530,7 @@ void add_entity_definition(CheckerInfo *i, AstNode *identifier, Entity *entity)
map_set(&i->definitions, key, entity);
}
-void add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
+b32 add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
if (!are_strings_equal(entity->token.string, make_string("_"))) {
Entity *insert_entity = scope_insert_entity(scope, entity);
if (insert_entity) {
@@ -541,18 +541,20 @@ void add_entity(Checker *c, Scope *scope, AstNode *identifier, Entity *entity) {
"\tat %.*s(%td:%td)",
LIT(entity->token.string),
LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column);
+ return false;
} else {
error(&c->error_collector, entity->token,
"Redeclararation of `%.*s` in this scope\n"
"\tat %.*s(%td:%td)",
LIT(entity->token.string),
LIT(entity->token.pos.file), entity->token.pos.line, entity->token.pos.column);
+ return false;
}
- return;
}
}
if (identifier != NULL)
add_entity_definition(&c->info, identifier, entity);
+ return true;
}
void add_entity_use(CheckerInfo *i, AstNode *identifier, Entity *entity) {
@@ -733,6 +735,10 @@ void check_parsed_files(Checker *c) {
case_ast_node(ld, LoadDecl, decl);
// NOTE(bill): ignore
case_end;
+ case_ast_node(fsl, ForeignSystemLibrary, decl);
+ // NOTE(bill): ignore
+ case_end;
+
default:
error(&c->error_collector, ast_node_token(decl), "Only declarations are allowed at file scope");
diff --git a/src/checker/entity.cpp b/src/checker/entity.cpp
index de4095766..8667aa0c7 100644
--- a/src/checker/entity.cpp
+++ b/src/checker/entity.cpp
@@ -6,6 +6,7 @@ enum BuiltinProcId;
ENTITY_KIND(Invalid), \
ENTITY_KIND(Constant), \
ENTITY_KIND(Variable), \
+ ENTITY_KIND(UsingVariable), \
ENTITY_KIND(TypeName), \
ENTITY_KIND(Procedure), \
ENTITY_KIND(Builtin), \
@@ -35,6 +36,7 @@ struct Entity {
Token token;
Type *type;
Entity *using_parent;
+ AstNode *using_expr;
union {
struct { ExactValue value; } Constant;
@@ -44,9 +46,9 @@ struct Entity {
b8 is_field; // Is struct field
b8 anonymous; // Variable is an anonymous
} Variable;
- struct {
- b8 used;
- } Procedure;
+ struct {} UsingVariable;
+ struct {} TypeName;
+ struct { b8 used; } Procedure;
struct { BuiltinProcId id; } Builtin;
};
};
@@ -72,6 +74,14 @@ Entity *make_entity_variable(gbAllocator a, Scope *scope, Token token, Type *typ
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_UsingVariable, parent->scope, token, type);
+ entity->using_parent = parent;
+ 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;
diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp
index c252871a8..b0ec95805 100644
--- a/src/checker/expr.cpp
+++ b/src/checker/expr.cpp
@@ -4,7 +4,7 @@ void check_expr_or_type (Checker *c, Operand *operand, AstNode *expre
ExprKind check_expr_base (Checker *c, Operand *operand, AstNode *expression, Type *type_hint = NULL);
Type * check_type (Checker *c, AstNode *expression, Type *named_type = NULL, CycleChecker *cycle_checker = NULL);
void check_type_decl (Checker *c, Entity *e, AstNode *type_expr, Type *def, CycleChecker *cycle_checker);
-void check_selector (Checker *c, Operand *operand, AstNode *node);
+Entity * check_selector (Checker *c, Operand *operand, AstNode *node);
void check_not_tuple (Checker *c, Operand *operand);
b32 check_value_is_expressible(Checker *c, ExactValue in_value, Type *type, ExactValue *out_value);
void convert_to_typed (Checker *c, Operand *operand, Type *target_type);
@@ -143,6 +143,7 @@ void populate_using_entity_map(Checker *c, AstNode *node, Type *t, Map<Entity *>
error(&c->error_collector, e->token, "`%.*s` is already declared in `%s`", LIT(name), str);
} else {
map_set(entity_map, key, f);
+ add_entity(c, c->context.scope, NULL, f);
if (f->Variable.anonymous) {
populate_using_entity_map(c, node, f->type, entity_map);
}
@@ -338,6 +339,7 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node, CycleChecke
} else {
map_set(&entity_map, key, e);
fields[field_index++] = e;
+ add_entity(c, c->context.scope, name, e);
}
add_entity_use(&c->info, name, e);
}
@@ -610,6 +612,11 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type, Cycl
o->mode = Addressing_Builtin;
break;
+ case Entity_UsingVariable:
+ // TODO(bill): Entity_UsingVariable: is this correct?
+ o->mode = Addressing_Variable;
+ break;
+
default:
GB_PANIC("Compiler error: Unknown EntityKind");
break;
@@ -727,6 +734,7 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c
check_open_scope(c, e);
check_struct_type(c, type, e, cycle_checker);
check_close_scope(c);
+ type->Struct.node = e;
goto end;
case_end;
@@ -736,6 +744,7 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type, CycleChecker *cycle_c
check_open_scope(c, e);
check_union_type(c, type, e, cycle_checker);
check_close_scope(c);
+ type->Union.node = e;
goto end;
case_end;
@@ -1746,7 +1755,7 @@ Selection lookup_field(Type *type_, String field_name, AddressingMode mode, Sele
return sel;
}
-void check_selector(Checker *c, Operand *operand, AstNode *node) {
+Entity *check_selector(Checker *c, Operand *operand, AstNode *node) {
GB_ASSERT(node->kind == AstNode_SelectorExpr);
ast_node(se, SelectorExpr, node);
@@ -1764,7 +1773,7 @@ void check_selector(Checker *c, Operand *operand, AstNode *node) {
error(&c->error_collector, ast_node_token(op_expr), "`%s` (`%s`) has no field `%s`", op_str, type_str, sel_str);
operand->mode = Addressing_Invalid;
operand->expr = node;
- return;
+ return NULL;
}
add_entity_use(&c->info, selector, entity);
@@ -1779,11 +1788,12 @@ void check_selector(Checker *c, Operand *operand, AstNode *node) {
if (operand->mode != Addressing_Variable)
operand->mode = Addressing_Value;
}
+ return entity;
} else {
operand->mode = Addressing_Invalid;
operand->expr = node;
}
-
+ return NULL;
}
b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) {
@@ -1886,7 +1896,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id)
return false;
}
- operand->mode = Addressing_Value;
+ operand->mode = Addressing_NoValue;
operand->type = NULL;
} break;
@@ -2967,6 +2977,7 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint
case AstNode_ArrayType:
case AstNode_VectorType:
case AstNode_StructType:
+ case AstNode_UnionType:
o->mode = Addressing_Type;
o->type = check_type(c, node);
break;
@@ -3240,6 +3251,13 @@ gbString write_expr_to_string(gbString str, AstNode *node) {
str = gb_string_appendc(str, "}");
case_end;
+ case_ast_node(st, UnionType, node);
+ str = gb_string_appendc(str, "union{");
+ // str = write_field_list_to_string(str, st->decl_list, ", ");
+ str = gb_string_appendc(str, "}");
+ case_end;
+
+
case_ast_node(et, EnumType, node);
str = gb_string_appendc(str, "enum ");
if (et->base_type != NULL) {
diff --git a/src/checker/stmt.cpp b/src/checker/stmt.cpp
index a0d1838a8..1bbaf1040 100644
--- a/src/checker/stmt.cpp
+++ b/src/checker/stmt.cpp
@@ -436,6 +436,107 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type, Cyc
+void check_var_decl(Checker *c, AstNode *node) {
+ ast_node(vd, VarDecl, node);
+ isize entity_count = vd->name_count;
+ isize entity_index = 0;
+ Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
+ switch (vd->kind) {
+ case Declaration_Mutable: {
+ Entity **new_entities = gb_alloc_array(c->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->Ident.token;
+ if (name->kind == AstNode_Ident) {
+ 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 = current_scope_lookup_entity(c->context.scope, str);
+ }
+ if (found == NULL) {
+ entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
+ if (!can_be_ignored) {
+ new_entities[new_entity_count++] = entity;
+ }
+ add_entity_definition(&c->info, name, entity);
+ } else {
+ entity = found;
+ }
+ } else {
+ error(&c->error_collector, token, "A variable declaration must be an identifier");
+ }
+ if (entity == NULL)
+ entity = make_entity_dummy_variable(c->allocator, c->global_scope, token);
+ entities[entity_index++] = entity;
+ }
+
+ Type *init_type = NULL;
+ if (vd->type) {
+ init_type = check_type(c, vd->type, 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->Variable.visited) {
+ e->type = t_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_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->context.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_Ident);
+ ExactValue v = {ExactValue_Invalid};
+ ast_node(i, Ident, name);
+ Entity *e = make_entity_constant(c->allocator, c->context.scope, i->token, NULL, v);
+ entities[entity_index++] = e;
+ check_const_decl(c, e, vd->type, value);
+ }
+
+ isize lhs_count = vd->name_count;
+ isize rhs_count = vd->value_count;
+
+ // TODO(bill): Better error messages or is this good enough?
+ if (rhs_count == 0 && vd->type == NULL) {
+ error(&c->error_collector, ast_node_token(node), "Missing type or initial expression");
+ } else if (lhs_count < rhs_count) {
+ error(&c->error_collector, 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->context.scope, name, entities[i]);
+ }
+ } break;
+
+ default:
+ error(&c->error_collector, ast_node_token(node), "Unknown variable declaration kind. Probably an invalid AST.");
+ return;
+ }
+}
+
void check_stmt(Checker *c, AstNode *node, u32 flags) {
switch (node->kind) {
@@ -688,16 +789,28 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
case_ast_node(us, UsingStmt, node);
switch (us->node->kind) {
case_ast_node(es, ExprStmt, us->node);
- AstNode *ident = es->expr;
- GB_ASSERT(ident->kind == AstNode_Ident);
- String name = ident->Ident.token.string;
+ Entity *e = NULL;
+
+ b32 is_selector = false;
+ AstNode *expr = unparen_expr(es->expr);
+ if (expr->kind == AstNode_Ident) {
+ String name = expr->Ident.token.string;
+ e = scope_lookup_entity(c, c->context.scope, name);
+ } else if (expr->kind == AstNode_SelectorExpr) {
+ Operand o = {};
+ check_expr_base(c, &o, expr->SelectorExpr.expr);
+ e = check_selector(c, &o, expr);
+ is_selector = true;
+ }
- Entity *e = scope_lookup_entity(c, c->context.scope, name);
if (e == NULL) {
error(&c->error_collector, us->token, "`using` applied to an unknown entity");
return;
}
+ gbString expr_str = expr_to_string(expr);
+ defer (gb_string_free(expr_str));
+
switch (e->kind) {
case Entity_TypeName: {
Type *t = get_base_type(e->type);
@@ -706,20 +819,34 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
Entity *f = t->Enum.fields[i];
Entity *found = scope_insert_entity(c->context.scope, f);
if (found != NULL) {
- error(&c->error_collector, us->token, "Namespace collision while `using` `%.*s` of the constant: %.*s", LIT(name), LIT(found->token.string));
+ error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of the constant: %.*s", expr_str, LIT(found->token.string));
return;
}
f->using_parent = e;
}
} else if (t->kind == Type_Struct) {
- for (isize i = 0; i < t->Struct.other_field_count; i++) {
- Entity *f = t->Struct.other_fields[i];
- Entity *found = scope_insert_entity(c->context.scope, f);
- if (found != NULL) {
- error(&c->error_collector, us->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(found->token.string));
- return;
+ Scope **found = map_get(&c->info.scopes, hash_pointer(t->Struct.node));
+ if (found != NULL) {
+ gb_for_array(i, (*found)->elements.entries) {
+ Entity *f = (*found)->elements.entries[i].value;
+ Entity *found = scope_insert_entity(c->context.scope, f);
+ if (found != NULL) {
+ error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
+ return;
+ }
+ f->using_parent = e;
+ }
+ } else {
+ for (isize i = 0; i < t->Struct.other_field_count; i++) {
+ // TODO(bill): using field types too
+ Entity *f = t->Struct.other_fields[i];
+ Entity *found = scope_insert_entity(c->context.scope, f);
+ if (found != NULL) {
+ error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(found->token.string));
+ return;
+ }
+ f->using_parent = e;
}
- f->using_parent = e;
}
}
} break;
@@ -733,13 +860,70 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
error(&c->error_collector, us->token, "`using` cannot be applied to a procedure");
break;
+ case Entity_Variable:
+ case Entity_UsingVariable: {
+ Type *t = get_base_type(type_deref(e->type));
+ if (t->kind == Type_Struct || t->kind == Type_Union) {
+ // IMPORTANT HACK(bill): Entity_(Struct|Union) overlap in some memory allowing
+ // for some variables to accessed to same
+ Scope **found = map_get(&c->info.scopes, hash_pointer(t->Struct.node));
+ GB_ASSERT(found != NULL);
+ gb_for_array(i, (*found)->elements.entries) {
+ Entity *f = (*found)->elements.entries[i].value;
+ if (f->kind == Entity_Variable) {
+ Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
+ if (is_selector) {
+ uvar->using_expr = expr;
+ }
+ Entity *prev = scope_insert_entity(c->context.scope, uvar);
+ if (prev != NULL) {
+ error(&c->error_collector, us->token, "Namespace collision while `using` `%s` of: %.*s", expr_str, LIT(prev->token.string));
+ return;
+ }
+ }
+ }
+ } else {
+ error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or union");
+ return;
+ }
+ } break;
+
default:
GB_PANIC("TODO(bill): using Ident");
}
case_end;
case_ast_node(vd, VarDecl, us->node);
- GB_PANIC("TODO(bill): using VarDecl");
+ if (vd->name_count > 1) {
+ error(&c->error_collector, us->token, "`using` can only be applied to one variable of the same type");
+ }
+ check_var_decl(c, us->node);
+ ast_node(i, Ident, vd->name_list);
+
+ String name = i->token.string;
+ Entity *e = scope_lookup_entity(c, c->context.scope, name);
+
+ Type *t = get_base_type(type_deref(e->type));
+ if (t->kind == Type_Struct || t->kind == Type_Union) {
+ // IMPORTANT HACK(bill): Entity_(Struct|Union) overlap in some memory allowing
+ // for some variables to accessed to same
+ Scope **found = map_get(&c->info.scopes, hash_pointer(t->Struct.node));
+ GB_ASSERT(found != NULL);
+ gb_for_array(i, (*found)->elements.entries) {
+ Entity *f = (*found)->elements.entries[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(&c->error_collector, us->token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
+ return;
+ }
+ }
+ }
+ } else {
+ error(&c->error_collector, us->token, "`using` can only be applied to variables of type struct or union");
+ return;
+ }
case_end;
@@ -755,103 +939,7 @@ void check_stmt(Checker *c, AstNode *node, u32 flags) {
case_ast_node(vd, VarDecl, node);
- isize entity_count = vd->name_count;
- isize entity_index = 0;
- Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
- switch (vd->kind) {
- case Declaration_Mutable: {
- Entity **new_entities = gb_alloc_array(c->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->Ident.token;
- if (name->kind == AstNode_Ident) {
- 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 = current_scope_lookup_entity(c->context.scope, str);
- }
- if (found == NULL) {
- entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
- if (!can_be_ignored) {
- new_entities[new_entity_count++] = entity;
- }
- add_entity_definition(&c->info, name, entity);
- } else {
- entity = found;
- }
- } else {
- error(&c->error_collector, token, "A variable declaration must be an identifier");
- }
- if (entity == NULL)
- entity = make_entity_dummy_variable(c->allocator, c->global_scope, token);
- entities[entity_index++] = entity;
- }
-
- Type *init_type = NULL;
- if (vd->type) {
- init_type = check_type(c, vd->type, 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->Variable.visited) {
- e->type = t_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_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->context.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_Ident);
- ExactValue v = {ExactValue_Invalid};
- ast_node(i, Ident, name);
- Entity *e = make_entity_constant(c->allocator, c->context.scope, i->token, NULL, v);
- entities[entity_index++] = e;
- check_const_decl(c, e, vd->type, value);
- }
-
- isize lhs_count = vd->name_count;
- isize rhs_count = vd->value_count;
-
- // TODO(bill): Better error messages or is this good enough?
- if (rhs_count == 0 && vd->type == NULL) {
- error(&c->error_collector, ast_node_token(node), "Missing type or initial expression");
- } else if (lhs_count < rhs_count) {
- error(&c->error_collector, 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->context.scope, name, entities[i]);
- }
- } break;
-
- default:
- error(&c->error_collector, ast_node_token(node), "Unknown variable declaration kind. Probably an invalid AST.");
- return;
- }
+ check_var_decl(c, node);
case_end;
case_ast_node(pd, ProcDecl, node);
diff --git a/src/checker/type.cpp b/src/checker/type.cpp
index bd1ff223e..d5cef4b07 100644
--- a/src/checker/type.cpp
+++ b/src/checker/type.cpp
@@ -107,18 +107,22 @@ struct Type {
// Theses are arrays
Entity **fields; // Entity_Variable
isize field_count; // == offset_count
+ AstNode *node;
+
i64 * offsets;
b32 are_offsets_set;
b32 is_packed;
Entity **other_fields; // Entity_Constant or Entity_TypeName
isize other_field_count;
+
} Struct;
struct {
- // IMPORTANT HACK(bill): The positions of fields and field_count
+ // IMPORTANT HACK(bill): The positions of fields, field_count, and node
// must be same for Struct and Union
Entity **fields; // Entity_Variable
isize field_count;
+ AstNode *node;
} Union;
struct { Type *elem; } Pointer;
struct {
diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp
index bf2470a28..b01a50d33 100644
--- a/src/codegen/ssa.cpp
+++ b/src/codegen/ssa.cpp
@@ -2034,6 +2034,30 @@ ssaValue *ssa_emit_deep_field_gep(ssaProcedure *proc, Type *type, ssaValue *e, S
}
+ssaValue *ssa_add_using_variable(ssaProcedure *proc, Entity *e) {
+ GB_ASSERT(e->kind == Entity_UsingVariable);
+ String name = e->token.string;
+ Entity *parent = e->using_parent;
+ ssaValue *p = NULL;
+ if (parent->kind == Entity_UsingVariable) {
+ p = ssa_add_using_variable(proc, parent);
+ }
+
+ Selection sel = lookup_field(parent->type, name, Addressing_Variable);
+ GB_ASSERT(sel.entity != NULL);
+ ssaValue **pv = map_get(&proc->module->values, hash_pointer(parent));
+ ssaValue *v = NULL;
+ if (pv != NULL) {
+ v = *pv;
+ } else {
+ v = ssa_build_addr(proc, e->using_expr).addr;
+ }
+ GB_ASSERT(v != NULL);
+ ssaValue *var = ssa_emit_deep_field_gep(proc, parent->type, v, sel);
+ map_set(&proc->module->values, hash_pointer(e), var);
+ return var;
+}
+
ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
switch (expr->kind) {
case_ast_node(i, Ident, expr);
@@ -2047,9 +2071,13 @@ ssaAddr ssa_build_addr(ssaProcedure *proc, AstNode *expr) {
ssaValue **found = map_get(&proc->module->values, hash_pointer(e));
if (found) {
v = *found;
- } else {
+ } else if (e->kind == Entity_UsingVariable) {
+ v = ssa_add_using_variable(proc, e);
+ }
+ if (v == NULL) {
GB_PANIC("Unknown value: %s, entity: %p\n", expr_to_string(expr), e);
}
+
return ssa_make_addr(v, expr);
case_end;
@@ -2286,6 +2314,13 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
case_ast_node(bs, EmptyStmt, node);
case_end;
+ case_ast_node(us, UsingStmt, node);
+ AstNode *decl = unparen_expr(us->node);
+ if (decl->kind == AstNode_VarDecl) {
+ ssa_build_stmt(proc, decl);
+ }
+ case_end;
+
case_ast_node(vd, VarDecl, node);
if (vd->kind == Declaration_Mutable) {
if (vd->name_count == vd->value_count) { // 1:1 assigment
diff --git a/src/main.cpp b/src/main.cpp
index 26ef255ce..b37ffad48 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -93,14 +93,26 @@ int main(int argc, char **argv) {
#if 1
#endif
+ gbString lib_str = gb_string_make(gb_heap_allocator(), "-lKernel32.lib");
+ char lib_str_buf[1024] = {};
+ gb_for_array(i, parser.system_libraries) {
+ String lib = parser.system_libraries[i];
+ isize len = gb_snprintf(lib_str_buf, gb_size_of(lib_str_buf),
+ " -l%.*s.lib", LIT(lib));
+ lib_str = gb_string_appendc(lib_str, lib_str_buf);
+ }
+
+
exit_code = win32_exec_command_line_app(
"clang -o %.*s.exe %.*s.bc "
"-Wno-override-module "
// "-nostartfiles "
- "-lKernel32.lib -lUser32.lib -lGdi32.lib -lOpengl32.lib "
+ "%s "
,
cast(int)base_name_len, output_name,
- cast(int)base_name_len, output_name);
+ cast(int)base_name_len, output_name,
+ lib_str);
+ gb_string_free(lib_str);
if (exit_code != 0)
return exit_code;
diff --git a/src/parser.cpp b/src/parser.cpp
index e3be11940..e4544dae9 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -32,6 +32,7 @@ struct AstFile {
AstScope *file_scope;
AstScope *curr_scope;
+ AstNode *curr_proc;
isize scope_level;
ErrorCollector error_collector;
@@ -60,6 +61,8 @@ struct Parser {
String init_fullpath;
gbArray(AstFile) files;
gbArray(String) loads;
+ gbArray(String) libraries;
+ gbArray(String) system_libraries;
isize load_index;
isize total_token_count;
};
@@ -193,6 +196,7 @@ AST_NODE_KIND(_DeclBegin, struct{}) \
}) \
AST_NODE_KIND(TypeDecl, struct { Token token; AstNode *name, *type; }) \
AST_NODE_KIND(LoadDecl, struct { Token token, filepath; }) \
+ AST_NODE_KIND(ForeignSystemLibrary, struct { Token token, filepath; }) \
AST_NODE_KIND(_DeclEnd, struct{}) \
AST_NODE_KIND(_TypeBegin, struct{}) \
AST_NODE_KIND(Field, struct { \
@@ -368,6 +372,8 @@ Token ast_node_token(AstNode *node) {
return node->TypeDecl.token;
case AstNode_LoadDecl:
return node->LoadDecl.token;
+ case AstNode_ForeignSystemLibrary:
+ return node->ForeignSystemLibrary.token;
case AstNode_Field: {
if (node->Field.name_list)
return ast_node_token(node->Field.name_list);
@@ -831,6 +837,12 @@ gb_inline AstNode *make_load_decl(AstFile *f, Token token, Token filepath) {
return result;
}
+gb_inline AstNode *make_foreign_system_library(AstFile *f, Token token, Token filepath) {
+ AstNode *result = make_node(f, AstNode_ForeignSystemLibrary);
+ result->ForeignSystemLibrary.token = token;
+ result->ForeignSystemLibrary.filepath = filepath;
+ return result;
+}
gb_inline b32 next_token(AstFile *f) {
if (f->cursor+1 < f->tokens + gb_array_count(f->tokens)) {
@@ -967,11 +979,15 @@ b32 expect_semicolon_after_stmt(AstFile *f, AstNode *s) {
}
if (!allow_token(f, Token_Semicolon)) {
- // CLEANUP(bill): Semicolon handling in parser
- ast_file_err(f, f->cursor[0],
- "Expected `;` after %.*s, got `%.*s`",
- LIT(ast_node_strings[s->kind]), LIT(token_strings[f->cursor[0].kind]));
- return false;
+ if (f->cursor[0].pos.line == f->cursor[-1].pos.line) {
+ if (f->cursor[0].kind != Token_CloseBrace) {
+ // CLEANUP(bill): Semicolon handling in parser
+ ast_file_err(f, f->cursor[0],
+ "Expected `;` after %.*s, got `%.*s`",
+ LIT(ast_node_strings[s->kind]), LIT(token_strings[f->cursor[0].kind]));
+ return false;
+ }
+ }
}
return true;
}
@@ -1203,7 +1219,10 @@ AstNode *parse_operand(AstFile *f, b32 lhs) {
// Parse Procedure Type or Literal
case Token_proc: {
AstScope *scope = NULL;
+ AstNode *curr_proc = f->curr_proc;
AstNode *type = parse_proc_type(f, &scope);
+ f->curr_proc = type;
+ defer (f->curr_proc = curr_proc);
u64 tags = 0;
String foreign_name = {};
@@ -1862,8 +1881,13 @@ AstNode *parse_identifier_or_type(AstFile *f) {
return make_enum_type(f, token, base_type, root, field_count);
}
- case Token_proc:
- return parse_proc_type(f, NULL);
+ case Token_proc: {
+ AstNode *curr_proc = f->curr_proc;
+ AstNode *type = parse_proc_type(f, NULL);
+ f->curr_proc = type;
+ f->curr_proc = curr_proc;
+ return type;
+ }
case Token_OpenParen: {
@@ -2126,10 +2150,14 @@ AstNode *parse_return_stmt(AstFile *f) {
Token token = expect_token(f, Token_return);
AstNode *result = NULL;
isize result_count = 0;
- if (f->cursor[0].kind != Token_Semicolon)
+
+ if (f->cursor[0].kind != Token_Semicolon && f->cursor[0].kind != Token_CloseBrace &&
+ f->cursor[0].pos.line == token.pos.line) {
result = parse_rhs_expr_list(f, &result_count);
- if (f->cursor[0].kind != Token_CloseBrace)
- expect_token(f, Token_Semicolon);
+ }
+ if (f->cursor[0].kind != Token_CloseBrace) {
+ expect_semicolon_after_stmt(f, result);
+ }
return make_return_stmt(f, token, result, result_count);
}
@@ -2248,11 +2276,15 @@ AstNode *parse_stmt(AstFile *f) {
b32 valid = false;
switch (node->kind) {
- case AstNode_ExprStmt:
- if (node->ExprStmt.expr->kind == AstNode_Ident) {
+ case AstNode_ExprStmt: {
+ AstNode *e = unparen_expr(node->ExprStmt.expr);
+ while (e->kind == AstNode_SelectorExpr) {
+ e = unparen_expr(e->SelectorExpr.selector);
+ }
+ if (e->kind == AstNode_Ident) {
valid = true;
}
- break;
+ } break;
case AstNode_VarDecl:
if (node->VarDecl.kind == Declaration_Mutable) {
valid = true;
@@ -2279,6 +2311,13 @@ AstNode *parse_stmt(AstFile *f) {
}
ast_file_err(f, token, "You cannot `load` within a procedure. This must be done at the file scope.");
return make_bad_decl(f, token, file_path);
+ } else if (are_strings_equal(s->TagStmt.name.string, make_string("foreign_system_library"))) {
+ Token file_path = expect_token(f, Token_String);
+ if (f->curr_scope == f->file_scope) {
+ return make_foreign_system_library(f, s->TagStmt.token, file_path);
+ }
+ ast_file_err(f, token, "You cannot using `foreign_system_library` within a procedure. This must be done at the file scope.");
+ return make_bad_decl(f, token, file_path);
} else if (are_strings_equal(s->TagStmt.name.string, make_string("thread_local"))) {
AstNode *var_decl = parse_simple_stmt(f);
if (var_decl->kind != AstNode_VarDecl ||
@@ -2387,6 +2426,8 @@ void destroy_ast_file(AstFile *f) {
b32 init_parser(Parser *p) {
gb_array_init(p->files, gb_heap_allocator());
gb_array_init(p->loads, gb_heap_allocator());
+ gb_array_init(p->libraries, gb_heap_allocator());
+ gb_array_init(p->system_libraries, gb_heap_allocator());
return true;
}
@@ -2402,6 +2443,8 @@ void destroy_parser(Parser *p) {
#endif
gb_array_free(p->files);
gb_array_free(p->loads);
+ gb_array_free(p->libraries);
+ gb_array_free(p->system_libraries);
}
// NOTE(bill): Returns true if it's added
@@ -2417,6 +2460,18 @@ b32 try_add_load_path(Parser *p, String import_file) {
return true;
}
+// NOTE(bill): Returns true if it's added
+b32 try_add_foreign_system_library_path(Parser *p, String import_file) {
+ gb_for_array(i, p->system_libraries) {
+ String import = p->system_libraries[i];
+ if (are_strings_equal(import, import_file)) {
+ return false;
+ }
+ }
+ gb_array_append(p->system_libraries, import_file);
+ return true;
+}
+
gb_global Rune illegal_import_runes[] = {
'"', '\'', '`', ' ',
'\\', // NOTE(bill): Disallow windows style filepaths
@@ -2480,7 +2535,7 @@ void parse_file(Parser *p, AstFile *f) {
String file_str = id->filepath.string;
if (!is_load_path_valid(file_str)) {
- ast_file_err(f, ast_node_token(node), "Invalid import path");
+ ast_file_err(f, ast_node_token(node), "Invalid `load` path");
continue;
}
@@ -2498,6 +2553,16 @@ void parse_file(Parser *p, AstFile *f) {
if (!try_add_load_path(p, import_file)) {
gb_free(gb_heap_allocator(), import_file.text);
}
+ } else if (node->kind == AstNode_ForeignSystemLibrary) {
+ auto *id = &node->ForeignSystemLibrary;
+ String file_str = id->filepath.string;
+
+ if (!is_load_path_valid(file_str)) {
+ ast_file_err(f, ast_node_token(node), "Invalid `foreign_system_library` path");
+ continue;
+ }
+
+ try_add_foreign_system_library_path(p, file_str);
}
}
}