aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGinger Bill <bill@gingerbill.org>2017-06-28 22:38:04 +0100
committerGinger Bill <bill@gingerbill.org>2017-06-28 22:38:04 +0100
commit9ca2246bac823022f39d495398456bcb142d50b9 (patch)
tree7d82fc39a6722ad2f216c7d652d17eb20c9e9834 /src
parent647e2cafd7d353a454fd1593d54fe09824a8e527 (diff)
Basic allowance for := and ::
Diffstat (limited to 'src')
-rw-r--r--src/check_stmt.cpp147
-rw-r--r--src/checker.cpp150
-rw-r--r--src/ir.cpp77
-rw-r--r--src/parser.cpp77
4 files changed, 449 insertions, 2 deletions
diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp
index 0eac4936b..21e960a06 100644
--- a/src/check_stmt.cpp
+++ b/src/check_stmt.cpp
@@ -1666,6 +1666,153 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
c->context = prev_context;
case_end;
+ case_ast_node(vd, ValueDecl, node);
+ if (vd->is_mutable) {
+ Entity **entities = gb_alloc_array(c->allocator, Entity *, vd->names.count);
+ isize entity_count = 0;
+
+ if (vd->flags & VarDeclFlag_thread_local) {
+ vd->flags &= ~VarDeclFlag_thread_local;
+ error(node, "`thread_local` may only be applied to a variable declaration");
+ }
+
+ for_array(i, vd->names) {
+ AstNode *name = vd->names[i];
+ Entity *entity = NULL;
+ if (name->kind != AstNode_Ident) {
+ error(name, "A variable declaration must be an identifier");
+ } else {
+ Token token = name->Ident;
+ String str = token.string;
+ Entity *found = NULL;
+ // NOTE(bill): Ignore assignments to `_`
+ if (str != "_") {
+ found = current_scope_lookup_entity(c->context.scope, str);
+ }
+ if (found == NULL) {
+ entity = make_entity_variable(c->allocator, c->context.scope, token, NULL, false);
+ entity->identifier = name;
+
+ AstNode *fl = c->context.curr_foreign_library;
+ if (fl != NULL) {
+ GB_ASSERT(fl->kind == AstNode_Ident);
+ entity->Variable.is_foreign = true;
+ entity->Variable.foreign_library_ident = fl;
+ }
+ } 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;
+ }
+ }
+ if (entity == NULL) {
+ entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
+ }
+ entity->parent_proc_decl = c->context.curr_proc_decl;
+ entities[entity_count++] = 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->flags & EntityFlag_Visited) {
+ e->type = t_invalid;
+ continue;
+ }
+ e->flags |= EntityFlag_Visited;
+
+ if (e->type == NULL) {
+ e->type = init_type;
+ }
+ }
+
+ check_arity_match(c, vd);
+ check_init_variables(c, entities, entity_count, vd->values, str_lit("variable declaration"));
+
+ for (isize i = 0; i < entity_count; i++) {
+ Entity *e = entities[i];
+ if (e->Variable.is_foreign) {
+ if (vd->values.count > 0) {
+ error(e->token, "A foreign variable declaration cannot have a default value");
+ }
+ init_entity_foreign_library(c, e);
+
+ String name = e->token.string;
+ auto *fp = &c->info.foreigns;
+ HashKey key = hash_string(name);
+ Entity **found = map_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_types_identical(this_type, other_type)) {
+ error(e->token,
+ "Foreign entity `%.*s` previously declared elsewhere with a different type\n"
+ "\tat %.*s(%td:%td)",
+ LIT(name), LIT(pos.file), pos.line, pos.column);
+ }
+ } else {
+ map_set(fp, key, e);
+ }
+ }
+ add_entity(c, c->context.scope, e->identifier, e);
+ }
+
+ if ((vd->flags & VarDeclFlag_using) != 0) {
+ Token token = ast_node_token(node);
+ if (vd->type != NULL && entity_count > 1) {
+ error(token, "`using` can only be applied to one variable of the same type");
+ // TODO(bill): Should a `continue` happen here?
+ }
+
+ for (isize entity_index = 0; entity_index < entity_count; entity_index++) {
+ Entity *e = entities[entity_index];
+ if (e == NULL) {
+ continue;
+ }
+ if (e->kind != Entity_Variable) {
+ continue;
+ }
+ bool is_immutable = e->Variable.is_immutable;
+ String name = e->token.string;
+ Type *t = base_type(type_deref(e->type));
+
+ if (is_type_struct(t) || is_type_raw_union(t)) {
+ Scope *scope = scope_of_node(&c->info, t->Record.node);
+ for_array(i, scope->elements.entries) {
+ Entity *f = scope->elements.entries[i].value;
+ if (f->kind == Entity_Variable) {
+ Entity *uvar = make_entity_using_variable(c->allocator, e, f->token, f->type);
+ uvar->Variable.is_immutable = is_immutable;
+ Entity *prev = scope_insert_entity(c->context.scope, uvar);
+ if (prev != NULL) {
+ error(token, "Namespace collision while `using` `%.*s` of: %.*s", LIT(name), LIT(prev->token.string));
+ return;
+ }
+ }
+ }
+ } else {
+ // NOTE(bill): skip the rest to remove extra errors
+ error(token, "`using` can only be applied to variables of type struct or raw_union");
+ return;
+ }
+ }
+ }
+ }
+ case_end;
+
case_ast_node(gd, GenDecl, node);
GB_ASSERT(!c->context.scope->is_file);
diff --git a/src/checker.cpp b/src/checker.cpp
index f490a2a23..238482060 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -1387,6 +1387,7 @@ void init_preload(Checker *c) {
bool check_arity_match(Checker *c, AstNodeValueSpec *s);
+bool check_arity_match(Checker *c, AstNodeValueDecl *vd);
void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_scope);
void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, bool is_file_scope);
@@ -1537,6 +1538,37 @@ bool check_arity_match(Checker *c, AstNodeValueSpec *spec) {
return true;
}
+
+bool check_arity_match(Checker *c, AstNodeValueDecl *vd) {
+ isize lhs = vd->names.count;
+ isize rhs = vd->values.count;
+
+ if (rhs == 0) {
+ if (vd->type == NULL) {
+ error(vd->names[0], "Missing type or initial expression");
+ return false;
+ }
+ } else if (lhs < rhs) {
+ if (lhs < vd->values.count) {
+ AstNode *n = vd->values[lhs];
+ gbString str = expr_to_string(n);
+ error(n, "Extra initial expression `%s`", str);
+ gb_string_free(str);
+ } else {
+ error(vd->names[0], "Extra initial expression");
+ }
+ return false;
+ } else if (lhs > rhs && rhs != 1) {
+ AstNode *n = vd->names[rhs];
+ gbString str = expr_to_string(n);
+ error(n, "Missing expression for `%s`", str);
+ gb_string_free(str);
+ return false;
+ }
+
+ return true;
+}
+
void check_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, bool is_file_scope) {
Operand operand = {Addressing_Invalid};
check_expr(c, &operand, ws->cond);
@@ -1595,6 +1627,124 @@ void check_collect_entities(Checker *c, Array<AstNode *> nodes, bool is_file_sco
}
case_end;
+ case_ast_node(vd, ValueDecl, decl);
+ if (vd->is_mutable) {
+ if (!c->context.scope->is_file) {
+ // NOTE(bill): local scope -> handle later and in order
+ break;
+ }
+
+ // NOTE(bill): You need to store the entity information here unline a constant declaration
+ isize entity_cap = vd->names.count;
+ isize entity_count = 0;
+ Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_cap);
+ DeclInfo *di = NULL;
+ if (vd->values.count > 0) {
+ di = make_declaration_info(heap_allocator(), c->context.scope, c->context.decl);
+ di->entities = entities;
+ di->type_expr = vd->type;
+ di->init_expr = vd->values[0];
+
+
+ if (vd->flags & VarDeclFlag_thread_local) {
+ error(decl, "#thread_local variable declarations cannot have initialization values");
+ }
+ }
+
+
+ for_array(i, vd->names) {
+ AstNode *name = vd->names[i];
+ AstNode *value = NULL;
+ if (i < vd->values.count) {
+ value = vd->values[i];
+ }
+ if (name->kind != AstNode_Ident) {
+ error(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
+ continue;
+ }
+ Entity *e = make_entity_variable(c->allocator, c->context.scope, name->Ident, NULL, false);
+ e->Variable.is_thread_local = (vd->flags & VarDeclFlag_thread_local) != 0;
+ e->identifier = name;
+
+ if (vd->flags & VarDeclFlag_using) {
+ vd->flags &= ~VarDeclFlag_using; // NOTE(bill): This error will be only caught once
+ error(name, "`using` is not allowed at the file scope");
+ }
+
+ AstNode *fl = c->context.curr_foreign_library;
+ if (fl != NULL) {
+ GB_ASSERT(fl->kind == AstNode_Ident);
+ e->Variable.is_foreign = true;
+ e->Variable.foreign_library_ident = fl;
+ }
+
+ entities[entity_count++] = e;
+
+ DeclInfo *d = di;
+ if (d == NULL) {
+ AstNode *init_expr = value;
+ d = make_declaration_info(heap_allocator(), e->scope, c->context.decl);
+ d->type_expr = vd->type;
+ d->init_expr = init_expr;
+ }
+
+ add_entity_and_decl_info(c, name, e, d);
+ }
+
+ if (di != NULL) {
+ di->entity_count = entity_count;
+ }
+
+ check_arity_match(c, vd);
+ } else {
+ for_array(i, vd->names) {
+ AstNode *name = vd->names[i];
+ if (name->kind != AstNode_Ident) {
+ error(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
+ continue;
+ }
+
+ AstNode *init = unparen_expr(vd->values[i]);
+
+ AstNode *fl = c->context.curr_foreign_library;
+ DeclInfo *d = make_declaration_info(c->allocator, c->context.scope, c->context.decl);
+ Entity *e = NULL;
+
+ if (is_ast_node_type(init)) {
+ e = make_entity_type_name(c->allocator, d->scope, name->Ident, NULL);
+ d->type_expr = init;
+ d->init_expr = init;
+ } else if (init->kind == AstNode_ProcLit) {
+ ast_node(pl, ProcLit, init);
+ e = make_entity_procedure(c->allocator, d->scope, name->Ident, NULL, pl->tags);
+ if (fl != NULL) {
+ GB_ASSERT(fl->kind == AstNode_Ident);
+ e->Procedure.foreign_library_ident = fl;
+ pl->tags |= ProcTag_foreign;
+ }
+ GB_PANIC("TODO(bill): Constant procedure literals");
+ d->proc_decl = init;
+ d->type_expr = pl->type;
+ } else {
+ e = make_entity_constant(c->allocator, d->scope, name->Ident, NULL, empty_exact_value);
+ d->type_expr = vd->type;
+ d->init_expr = init;
+ }
+ e->identifier = name;
+
+ if (fl != NULL && e->kind != Entity_Procedure) {
+ error(name, "Only procedures and variables are allowed to be in a foreign block");
+ // continue;
+ }
+
+
+ add_entity_and_decl_info(c, name, e, d);
+ }
+
+ check_arity_match(c, vd);
+ }
+ case_end;
+
case_ast_node(gd, GenDecl, decl);
AstNodeValueSpec empty_spec = {};
AstNodeValueSpec *last_spec = NULL;
diff --git a/src/ir.cpp b/src/ir.cpp
index 69b4c75f6..685730980 100644
--- a/src/ir.cpp
+++ b/src/ir.cpp
@@ -5949,6 +5949,83 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) {
ir_build_assign_op(proc, addr, v_one, op);
case_end;
+ case_ast_node(vd, ValueDecl, node);
+ if (vd->is_mutable) {
+ irModule *m = proc->module;
+ gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
+
+ if (vd->values.count == 0) { // declared and zero-initialized
+ for_array(i, vd->names) {
+ AstNode *name = vd->names[i];
+ if (!ir_is_blank_ident(name)) {
+ ir_add_local_for_identifier(proc, name, true);
+ }
+ }
+ } else { // Tuple(s)
+ Array<irAddr> lvals = {};
+ Array<irValue *> inits = {};
+ array_init(&lvals, m->tmp_allocator, vd->names.count);
+ array_init(&inits, m->tmp_allocator, vd->names.count);
+
+ for_array(i, vd->names) {
+ AstNode *name = vd->names[i];
+ irAddr lval = ir_addr(NULL);
+ if (!ir_is_blank_ident(name)) {
+ ir_add_local_for_identifier(proc, name, false);
+ lval = ir_build_addr(proc, name);
+ }
+
+ array_add(&lvals, lval);
+ }
+
+ for_array(i, vd->values) {
+ irValue *init = ir_build_expr(proc, vd->values[i]);
+ Type *t = ir_type(init);
+ if (t->kind == Type_Tuple) {
+ for (isize i = 0; i < t->Tuple.variable_count; i++) {
+ Entity *e = t->Tuple.variables[i];
+ irValue *v = ir_emit_struct_ev(proc, init, i);
+ array_add(&inits, v);
+ }
+ } else {
+ array_add(&inits, init);
+ }
+ }
+
+
+ for_array(i, inits) {
+ ir_addr_store(proc, lvals[i], inits[i]);
+ }
+ }
+
+ gb_temp_arena_memory_end(tmp);
+ } else {
+ for_array(i, vd->names) {
+ AstNode *ident = vd->names[i];
+ GB_ASSERT(ident->kind == AstNode_Ident);
+ Entity *e = entity_of_ident(proc->module->info, ident);
+ GB_ASSERT(e != NULL);
+ if (e->kind == Entity_TypeName) {
+ // NOTE(bill): Generate a new name
+ // parent_proc.name-guid
+ String ts_name = e->token.string;
+ isize name_len = proc->name.len + 1 + ts_name.len + 1 + 10 + 1;
+ u8 *name_text = gb_alloc_array(proc->module->allocator, u8, name_len);
+ i32 guid = cast(i32)proc->module->members.entries.count;
+ name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s.%.*s-%d", LIT(proc->name), LIT(ts_name), guid);
+ String name = make_string(name_text, name_len-1);
+
+ irValue *value = ir_value_type_name(proc->module->allocator,
+ name, e->type);
+ map_set(&proc->module->entity_names, hash_entity(e), name);
+ ir_gen_global_type_name(proc->module, e, name);
+ } else if (e->kind == Entity_Procedure) {
+ GB_PANIC("TODO(bill): Procedure values");
+ }
+ }
+ }
+ case_end;
+
case_ast_node(gd, GenDecl, node);
for_array(i, gd->specs) {
AstNode *spec = gd->specs[i];
diff --git a/src/parser.cpp b/src/parser.cpp
index f395b382a..2d12c3a6b 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -337,6 +337,15 @@ AST_NODE_KIND(_DeclBegin, "", i32) \
u64 flags; \
CommentGroup docs; \
}) \
+ AST_NODE_KIND(ValueDecl, "value declaration", struct { \
+ Array<AstNode *> names; \
+ AstNode * type; \
+ Array<AstNode *> values; \
+ u64 flags; \
+ bool is_mutable; \
+ CommentGroup docs; \
+ CommentGroup comment; \
+ }) \
AST_NODE_KIND(ValueSpec, "value specification", struct { \
Array<AstNode *> names; \
AstNode * type; \
@@ -577,6 +586,7 @@ Token ast_node_token(AstNode *node) {
case AstNode_Label: return node->Label.token;
case AstNode_GenDecl: return node->GenDecl.token;
+ case AstNode_ValueDecl: return ast_node_token(node->ValueDecl.names[0]);
case AstNode_ValueSpec: return ast_node_token(node->ValueSpec.names[0]);
case AstNode_ImportSpec: return node->ImportSpec.import_name;
case AstNode_TypeSpec: return ast_node_token(node->TypeSpec.name);
@@ -817,6 +827,11 @@ AstNode *clone_ast_node(gbAllocator a, AstNode *node) {
case AstNode_GenDecl:
n->GenDecl.specs = clone_ast_node_array(a, n->GenDecl.specs);
break;
+ case AstNode_ValueDecl:
+ n->ValueDecl.names = clone_ast_node_array(a, n->ValueDecl.names);
+ n->ValueDecl.type = clone_ast_node(a, n->ValueDecl.type);
+ n->ValueDecl.values = clone_ast_node_array(a, n->ValueDecl.values);
+ break;
case AstNode_ValueSpec:
n->ValueSpec.names = clone_ast_node_array(a, n->ValueSpec.names);
n->ValueSpec.type = clone_ast_node(a, n->ValueSpec.type);
@@ -1531,6 +1546,18 @@ AstNode *ast_gen_decl(AstFile *f, Token token, Token open, Token close, Array<As
return result;
}
+AstNode *ast_value_decl(AstFile *f, Array<AstNode *> names, AstNode *type, Array<AstNode *> values, bool is_mutable,
+ CommentGroup docs, CommentGroup comment) {
+ AstNode *result = make_ast_node(f, AstNode_ValueDecl);
+ result->ValueDecl.names = names;
+ result->ValueDecl.type = type;
+ result->ValueDecl.values = values;
+ result->ValueDecl.is_mutable = is_mutable;
+ result->ValueDecl.docs = docs;
+ result->ValueDecl.comment = comment;
+ return result;
+}
+
AstNode *ast_value_spec(AstFile *f, Array<AstNode *> names, AstNode *type, Array<AstNode *> values,
CommentGroup docs, CommentGroup comment) {
AstNode *result = make_ast_node(f, AstNode_ValueSpec);
@@ -3081,7 +3108,52 @@ AstNode *parse_decl(AstFile *f) {
return parse_gen_decl(f, token, func);
}
+AstNode *parse_value_decl(AstFile *f, Array<AstNode *> names, CommentGroup docs) {
+ bool is_mutable = true;
+ AstNode *type = NULL;
+ Array<AstNode *> values = {};
+
+ Token colon = expect_token_after(f, Token_Colon, "identifier list");
+ type = parse_type_attempt(f);
+
+ if (f->curr_token.kind == Token_Eq ||
+ f->curr_token.kind == Token_Colon) {
+ Token sep = f->curr_token; next_token(f);
+ is_mutable = sep.kind != Token_Colon;
+ values = parse_rhs_expr_list(f);
+ if (values.count > names.count) {
+ syntax_error(f->curr_token, "Too many values on the right hand side of the declaration");
+ } else if (values.count < names.count && !is_mutable) {
+ syntax_error(f->curr_token, "All constant declarations must be defined");
+ } else if (values.count == 0) {
+ syntax_error(f->curr_token, "Expected an expression for this declaration");
+ }
+ }
+
+
+ if (is_mutable) {
+ if (type == NULL && values.count == 0) {
+ syntax_error(f->curr_token, "Missing variable type or initialization");
+ return ast_bad_decl(f, f->curr_token, f->curr_token);
+ }
+ } else {
+ if (type == NULL && values.count == 0 && names.count > 0) {
+ syntax_error(f->curr_token, "Missing constant value");
+ return ast_bad_decl(f, f->curr_token, f->curr_token);
+ }
+ }
+
+ if (values.data == NULL) {
+ values = make_ast_node_array(f);
+ }
+
+ if (f->expr_level >= 0) {
+ expect_semicolon(f, NULL);
+ }
+
+ return ast_value_decl(f, names, type, values, is_mutable, docs, f->line_comment);
+}
AstNode *parse_simple_stmt(AstFile *f, StmtAllowFlag flags) {
Token token = f->curr_token;
@@ -3091,6 +3163,8 @@ AstNode *parse_simple_stmt(AstFile *f, StmtAllowFlag flags) {
return parse_decl(f);
}
+ CommentGroup docs = f->lead_comment;
+
Array<AstNode *> lhs = parse_lhs_expr_list(f);
token = f->curr_token;
switch (token.kind) {
@@ -3163,8 +3237,7 @@ AstNode *parse_simple_stmt(AstFile *f, StmtAllowFlag flags) {
} break;
}
}
-
- // return parse_value_decl(f, lhs);
+ return parse_value_decl(f, lhs, docs);
}
if (lhs.count > 1) {