aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGinger Bill <bill@gingerbill.org>2016-12-19 11:56:45 +0000
committerGinger Bill <bill@gingerbill.org>2016-12-19 11:56:45 +0000
commitf5eeecaca5842e4594ad2ed482c529a683bfb012 (patch)
tree25d9a45ca306f16f8bfc6af339c277ec8b163277 /src
parent77e219d442b54860979edeaa378d99b8e74d2ebd (diff)
Begin generic declarations for lists of specifications
Diffstat (limited to 'src')
-rw-r--r--src/checker/checker.c93
-rw-r--r--src/checker/expr.c83
-rw-r--r--src/checker/stmt.c84
-rw-r--r--src/parser.c479
-rw-r--r--src/ssa.c72
5 files changed, 565 insertions, 246 deletions
diff --git a/src/checker/checker.c b/src/checker/checker.c
index 3e0606d6a..3af0c2f8b 100644
--- a/src/checker/checker.c
+++ b/src/checker/checker.c
@@ -1106,6 +1106,94 @@ void check_global_collect_entities_from_file(Checker *c, Scope *parent_scope, As
switch (decl->kind) {
case_ast_node(bd, BadDecl, decl);
case_end;
+ case_ast_node(gd, GenericDecl, decl);
+ if (!parent_scope->is_file) {
+ // NOTE(bill): Within a procedure, variables must be in order
+ continue;
+ }
+
+ for_array(spec_index, gd->specs) {
+ AstNode *spec = gd->specs.e[spec_index];
+ switch (spec->kind) {
+ case_ast_node(vs, ValueSpec, spec);
+ switch (vs->keyword) {
+ case Token_var: {
+ // NOTE(bill): You need to store the entity information here unline a constant declaration
+ isize entity_count = vs->names.count;
+ isize entity_index = 0;
+ Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
+ DeclInfo *di = NULL;
+ if (vs->values.count > 0) {
+ di = make_declaration_info(heap_allocator(), parent_scope);
+ di->entities = entities;
+ di->entity_count = entity_count;
+ di->type_expr = vs->type;
+ di->init_expr = vs->values.e[0];
+ }
+
+ for_array(i, vs->names) {
+ AstNode *name = vs->names.e[i];
+ AstNode *value = NULL;
+ if (i < vs->values.count) {
+ value = vs->values.e[i];
+ }
+ if (name->kind != AstNode_Ident) {
+ error_node(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, parent_scope, name->Ident, NULL);
+ e->identifier = name;
+ entities[entity_index++] = e;
+
+ DeclInfo *d = di;
+ if (d == NULL) {
+ AstNode *init_expr = value;
+ d = make_declaration_info(heap_allocator(), e->scope);
+ d->type_expr = vs->type;
+ d->init_expr = init_expr;
+ d->var_decl_tags = gd->tags;
+ }
+
+ add_entity_and_decl_info(c, name, e, d);
+ }
+ } break;
+
+ case Token_const: {
+ for_array(i, vs->values) {
+ AstNode *name = vs->names.e[i];
+ AstNode *value = unparen_expr(vs->values.e[i]);
+ if (name->kind != AstNode_Ident) {
+ error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
+ continue;
+ }
+
+ ExactValue v = {ExactValue_Invalid};
+ Entity *e = make_entity_constant(c->allocator, parent_scope, name->Ident, NULL, v);
+ e->identifier = name;
+ DeclInfo *di = make_declaration_info(c->allocator, e->scope);
+ di->type_expr = vs->type;
+ di->init_expr = value;
+ add_entity_and_decl_info(c, name, e, di);
+ }
+
+ isize lhs_count = vs->names.count;
+ isize rhs_count = vs->values.count;
+
+ if (rhs_count == 0 && vs->type == NULL) {
+ error_node(decl, "Missing type or initial expression");
+ } else if (lhs_count < rhs_count) {
+ error_node(decl, "Extra initial expression");
+ }
+ } break;
+ }
+ case_end;
+
+ default:
+ error(ast_node_token(spec), "Invalid specification in declaration: `%.*s`", LIT(ast_node_strings[spec->kind]));
+ break;
+ }
+ }
+ case_end;
case_ast_node(id, ImportDecl, decl);
if (!parent_scope->is_file) {
// NOTE(bill): _Should_ be caught by the parser
@@ -1428,7 +1516,10 @@ void check_parsed_files(Checker *c) {
ImplicitValueInfo *ivi = &implicit_value_infos[i];
Entity *backing = scope_lookup_entity(e->scope, ivi->backing_name);
- GB_ASSERT(backing != NULL);
+ // GB_ASSERT(backing != NULL);
+ if (backing == NULL) {
+ gb_exit(1);
+ }
e->ImplicitValue.backing = backing;
}
diff --git a/src/checker/expr.c b/src/checker/expr.c
index 8900cb4a7..3f8215f55 100644
--- a/src/checker/expr.c
+++ b/src/checker/expr.c
@@ -80,6 +80,89 @@ void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntitie
case_ast_node(ws, WhenStmt, node);
// Will be handled later
case_end;
+ case_ast_node(gd, GenericDecl, node);
+ for_array(spec_index, gd->specs) {
+ AstNode *spec = gd->specs.e[spec_index];
+ switch (spec->kind) {
+ case_ast_node(vs, ValueSpec, spec);
+ switch (vs->keyword) {
+ case Token_var:
+ break;
+
+ case Token_const: {
+ gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
+
+ isize entity_count = vs->names.count;
+ isize entity_index = 0;
+ Entity **entities = gb_alloc_array(c->tmp_allocator, Entity *, entity_count);
+
+ for_array(i, vs->values) {
+ AstNode *name = vs->names.e[i];
+ AstNode *value = unparen_expr(vs->values.e[i]);
+ if (name->kind != AstNode_Ident) {
+ error_node(name, "A declaration's name must be an identifier, got %.*s", LIT(ast_node_strings[name->kind]));
+ entities[entity_index++] = NULL;
+ continue;
+ }
+
+ ExactValue v = {ExactValue_Invalid};
+ Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v);
+ e->identifier = name;
+ entities[entity_index++] = e;
+ DeclInfo *d = make_declaration_info(c->allocator, e->scope);
+ d->type_expr = vs->type;
+ d->init_expr = value;
+ add_entity_and_decl_info(c, name, e, d);
+
+ DelayedEntity delay = {name, e, d};
+ array_add(delayed_entities, delay);
+ }
+
+ isize lhs_count = vs->names.count;
+ isize rhs_count = vs->values.count;
+
+ // TODO(bill): Better error messages or is this good enough?
+ if (rhs_count == 0 && vs->type == NULL) {
+ error_node(node, "Missing type or initial expression");
+ } else if (lhs_count < rhs_count) {
+ error_node(node, "Extra initial expression");
+ }
+
+ if (dof != NULL) {
+ // NOTE(bill): Within a record
+ for_array(i, vs->names) {
+ Entity *e = entities[i];
+ if (e == NULL) {
+ continue;
+ }
+ AstNode *name = vs->names.e[i];
+ if (name->kind != AstNode_Ident) {
+ continue;
+ }
+ Token name_token = name->Ident;
+ if (str_eq(name_token.string, str_lit("_"))) {
+ dof->other_fields[dof->other_field_index++] = e;
+ } else {
+ HashKey key = hash_string(name_token.string);
+ if (map_entity_get(dof->entity_map, key) != NULL) {
+ // TODO(bill): Scope checking already checks the declaration
+ error(name_token, "`%.*s` is already declared in this record", LIT(name_token.string));
+ } else {
+ map_entity_set(dof->entity_map, key, e);
+ dof->other_fields[dof->other_field_index++] = e;
+ }
+ add_entity(c, c->context.scope, name, e);
+ }
+ }
+ }
+
+ gb_temp_arena_memory_end(tmp);
+ } break;
+ }
+ case_end;
+ }
+ }
+ case_end;
case_ast_node(cd, ConstDecl, node);
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena);
diff --git a/src/checker/stmt.c b/src/checker/stmt.c
index f1074e2e6..7e1578987 100644
--- a/src/checker/stmt.c
+++ b/src/checker/stmt.c
@@ -1074,6 +1074,90 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) {
check_var_decl_node(c, node);
case_end;
+ case_ast_node(gd, GenericDecl, node);
+ for_array(spec_index, gd->specs) {
+ AstNode *spec = gd->specs.e[spec_index];
+ switch (spec->kind) {
+ case_ast_node(vs, ValueSpec, spec);
+ switch (vs->keyword) {
+ case Token_var: {
+ isize entity_count = vs->names.count;
+ isize entity_index = 0;
+ Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count);
+
+ for_array(i, vs->names) {
+ AstNode *name = vs->names.e[i];
+ Entity *entity = NULL;
+ if (name->kind == AstNode_Ident) {
+ Token token = name->Ident;
+ String str = token.string;
+ Entity *found = NULL;
+ // NOTE(bill): Ignore assignments to `_`
+ if (str_ne(str, str_lit("_"))) {
+ found = current_scope_lookup_entity(c->context.scope, str);
+ }
+ if (found == NULL) {
+ entity = make_entity_variable(c->allocator, c->context.scope, token, NULL);
+ add_entity_definition(&c->info, name, entity);
+ } else {
+ TokenPos pos = found->token.pos;
+ error(token,
+ "Redeclaration of `%.*s` in this scope\n"
+ "\tat %.*s(%td:%td)",
+ LIT(str), LIT(pos.file), pos.line, pos.column);
+ entity = found;
+ }
+ } else {
+ error_node(name, "A variable declaration must be an identifier");
+ }
+ if (entity == NULL) {
+ entity = make_entity_dummy_variable(c->allocator, c->global_scope, ast_node_token(name));
+ }
+ entities[entity_index++] = entity;
+ }
+
+ Type *init_type = NULL;
+ if (vs->type) {
+ init_type = check_type_extra(c, vs->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_init_variables(c, entities, entity_count, vs->values, str_lit("variable declaration"));
+
+ for_array(i, vs->names) {
+ if (entities[i] != NULL) {
+ add_entity(c, c->context.scope, vs->names.e[i], entities[i]);
+ }
+ }
+ } break;
+
+ case Token_const:
+ break;
+ }
+ case_end;
+
+ default:
+ error(ast_node_token(spec), "Invalid specification in declaration: `%.*s`", LIT(ast_node_strings[spec->kind]));
+ break;
+ }
+ }
+ case_end;
+
case_ast_node(cd, ConstDecl, node);
// NOTE(bill): Handled elsewhere
case_end;
diff --git a/src/parser.c b/src/parser.c
index c8329aabe..f8460e824 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -224,37 +224,53 @@ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \
\
AST_NODE_KIND(_ComplexStmtEnd, "", i32) \
AST_NODE_KIND(_StmtEnd, "", i32) \
+AST_NODE_KIND(_SpecBegin, "", i32) \
+ AST_NODE_KIND(ValueSpec, "value specification", struct { \
+ TokenKind keyword; \
+ AstNodeArray names; \
+ AstNode * type; \
+ AstNodeArray values; \
+ }) \
+AST_NODE_KIND(_SpecEnd, "", i32) \
AST_NODE_KIND(_DeclBegin, "", i32) \
- AST_NODE_KIND(BadDecl, "bad declaration", struct { Token begin, end; }) \
+ AST_NODE_KIND(BadDecl, "bad declaration", struct { Token begin, end; }) \
+ AST_NODE_KIND(GenericDecl, "generic declaration", struct { \
+ Token token; \
+ Token open, close; \
+ AstNodeArray specs; \
+ u64 tags; \
+ bool is_using; \
+ }) \
AST_NODE_KIND(VarDecl, "variable declaration", struct { \
- u64 tags; \
- bool is_using; \
- AstNodeArray names; \
- AstNode * type; \
- AstNodeArray values; \
- AstNode * note; \
+ u64 tags; \
+ bool is_using; \
+ AstNodeArray names; \
+ AstNode * type; \
+ AstNodeArray values; \
+ AstNode * note; \
}) \
AST_NODE_KIND(ConstDecl, "constant declaration", struct { \
- u64 tags; \
- AstNodeArray names; \
- AstNode * type; \
- AstNodeArray values; \
- AstNode * note; \
- }) \
- AST_NODE_KIND(ProcDecl, "procedure declaration", struct { \
- AstNode *name; \
- AstNode *type; \
- AstNode *body; \
- u64 tags; \
- String foreign_name; \
- String link_name; \
- AstNode *note; \
+ u64 tags; \
+ AstNodeArray names; \
+ AstNode * type; \
+ AstNodeArray values; \
+ AstNode * note; \
}) \
AST_NODE_KIND(TypeDecl, "type declaration", struct { \
- Token token; \
- AstNode *name, *type; \
+ Token token; \
+ AstNode *name; \
+ AstNode *type; \
AstNode *note; \
}) \
+ AST_NODE_KIND(ProcDecl, "procedure declaration", struct { \
+ AstNode *name; \
+ AstNode *type; \
+ AstNode *body; \
+ u64 tags; \
+ String foreign_name; \
+ String link_name; \
+ AstNode *note; \
+ }) \
AST_NODE_KIND(ImportDecl, "import declaration", struct { \
Token token, relpath; \
String fullpath; \
@@ -459,20 +475,27 @@ Token ast_node_token(AstNode *node) {
return node->PushAllocator.token;
case AstNode_PushContext:
return node->PushContext.token;
+
case AstNode_BadDecl:
return node->BadDecl.begin;
+ case AstNode_GenericDecl:
+ return node->GenericDecl.token;
case AstNode_VarDecl:
return ast_node_token(node->VarDecl.names.e[0]);
case AstNode_ConstDecl:
return ast_node_token(node->ConstDecl.names.e[0]);
case AstNode_ProcDecl:
- return node->ProcDecl.name->Ident;
+ return ast_node_token(node->ProcDecl.name);
case AstNode_TypeDecl:
- return node->TypeDecl.token;
+ return ast_node_token(node->TypeDecl.name);
case AstNode_ImportDecl:
return node->ImportDecl.token;
case AstNode_ForeignLibrary:
return node->ForeignLibrary.token;
+
+ case AstNode_ValueSpec:
+ return ast_node_token(node->ValueSpec.names.e[0]);
+
case AstNode_Parameter: {
if (node->Parameter.names.count > 0) {
return ast_node_token(node->Parameter.names.e[0]);
@@ -1033,6 +1056,29 @@ AstNode *make_foreign_library(AstFile *f, Token token, Token filepath, AstNode *
return result;
}
+
+AstNode *make_generic_decl(AstFile *f, Token token, Token open, Token close, AstNodeArray specs, u64 tags, bool is_using) {
+ AstNode *result = make_node(f, AstNode_GenericDecl);
+ result->GenericDecl.token = token;
+ result->GenericDecl.open = open;
+ result->GenericDecl.close = close;
+ result->GenericDecl.specs = specs;
+ result->GenericDecl.tags = tags;
+ result->GenericDecl.is_using = is_using;
+ return result;
+}
+
+AstNode *make_value_spec(AstFile *f, TokenKind keyword, AstNodeArray names, AstNode *type, AstNodeArray values) {
+ AstNode *result = make_node(f, AstNode_ValueSpec);
+ result->ValueSpec.keyword = keyword;
+ result->ValueSpec.names = names;
+ result->ValueSpec.type = type;
+ result->ValueSpec.values = values;
+ return result;
+}
+
+
+
bool next_token(AstFile *f) {
if (f->curr_token_index+1 < f->tokens.count) {
if (f->curr_token.kind != Token_Comment) {
@@ -1054,16 +1100,9 @@ Token expect_token(AstFile *f, TokenKind kind) {
Token prev = f->curr_token;
if (prev.kind != kind) {
String p = token_strings[prev.kind];
- if (prev.kind == Token_Semicolon &&
- str_eq(prev.string, str_lit("\n"))) {
- syntax_error(f->curr_token, "Expected `%.*s`, got newline",
- LIT(token_strings[kind]),
- LIT(p));
- } else {
- syntax_error(f->curr_token, "Expected `%.*s`, got `%.*s`",
- LIT(token_strings[kind]),
- LIT(token_strings[prev.kind]));
- }
+ syntax_error(f->curr_token, "Expected `%.*s`, got `%.*s`",
+ LIT(token_strings[kind]),
+ LIT(token_strings[prev.kind]));
}
next_token(f);
return prev;
@@ -1073,10 +1112,6 @@ Token expect_token_after(AstFile *f, TokenKind kind, char *msg) {
Token prev = f->curr_token;
if (prev.kind != kind) {
String p = token_strings[prev.kind];
- if (prev.kind == Token_Semicolon &&
- str_eq(prev.string, str_lit("\n"))) {
- p = str_lit("newline");
- }
syntax_error(f->curr_token, "Expected `%.*s` after %s, got `%.*s`",
LIT(token_strings[kind]),
msg,
@@ -1136,6 +1171,11 @@ void fix_advance_to_next_stmt(AstFile *f) {
case Token_Semicolon:
return;
+ case Token_var:
+ case Token_const:
+ case Token_type:
+ case Token_proc:
+
case Token_if:
case Token_when:
case Token_return:
@@ -1204,8 +1244,9 @@ void expect_semicolon(AstFile *f, AstNode *s) {
break;
}
- syntax_error(prev_token, "Expected `;` after %.*s, got %.*s",
- LIT(ast_node_strings[s->kind]), LIT(token_strings[prev_token.kind]));
+ syntax_error(prev_token, "Expected `;` after %.*s, got %.*s %d %d",
+ LIT(ast_node_strings[s->kind]), LIT(token_strings[prev_token.kind]),
+ Token_Semicolon, prev_token.kind);
} else {
syntax_error(prev_token, "Expected `;`");
}
@@ -1831,6 +1872,21 @@ AstNodeArray parse_rhs_expr_list(AstFile *f) {
return parse_expr_list(f, false);
}
+AstNodeArray parse_identfier_list(AstFile *f) {
+ AstNodeArray list = make_ast_node_array(f);
+
+ do {
+ array_add(&list, parse_identifier(f));
+ if (f->curr_token.kind != Token_Comma ||
+ f->curr_token.kind == Token_EOF) {
+ break;
+ }
+ next_token(f);
+ } while (true);
+
+ return list;
+}
+
void parse_check_name_list_for_reserves(AstFile *f, AstNodeArray names) {
for_array(i, names) {
AstNode *name = names.e[i];
@@ -1845,13 +1901,126 @@ void parse_check_name_list_for_reserves(AstFile *f, AstNodeArray names) {
}
}
-AstNode *parse_value_decl(AstFile *f);
+AstNode *parse_type_attempt(AstFile *f) {
+ AstNode *type = parse_identifier_or_type(f);
+ if (type != NULL) {
+ // TODO(bill): Handle?
+ }
+ return type;
+}
+
+AstNode *parse_type(AstFile *f) {
+ AstNode *type = parse_type_attempt(f);
+ if (type == NULL) {
+ Token token = f->curr_token;
+ syntax_error(token, "Expected a type");
+ next_token(f);
+ return make_bad_expr(f, token, f->curr_token);
+ }
+ return type;
+}
+
+
+#define PARSE_SPEC_PROC(name) AstNode *(name)(AstFile *f, TokenKind keyword)
+typedef PARSE_SPEC_PROC(*ParserSpecProc);
+
+
+AstNode *parse_generic_decl(AstFile *f, TokenKind keyword, ParserSpecProc spec_proc) {
+ Token token = expect_token(f, keyword);
+ Token open = {0}, close = {0};
+ AstNodeArray specs = {0};
+ if (f->curr_token.kind == Token_OpenParen) {
+ open = expect_token(f, Token_OpenParen);
+ array_init(&specs, heap_allocator());
+
+ while (f->curr_token.kind != Token_CloseParen &&
+ f->curr_token.kind != Token_EOF) {
+ AstNode *spec = spec_proc(f, keyword);
+ array_add(&specs, spec);
+ expect_semicolon(f, spec);
+ }
+
+ close = expect_token(f, Token_CloseParen);
+ } else {
+ array_init_reserve(&specs, heap_allocator(), 1);
+ array_add(&specs, spec_proc(f, keyword));
+ }
+
+ return make_generic_decl(f, token, open, close, specs, 0, false);
+}
+
+PARSE_SPEC_PROC(parse_value_spec) {
+ AstNodeArray names = parse_identfier_list(f);
+ parse_check_name_list_for_reserves(f, names);
+ AstNode *type = parse_type_attempt(f);
+ AstNodeArray values = {0};
+
+ if (allow_token(f, Token_Eq)) {
+ 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");
+ }
+
+ if (keyword == Token_const) {
+ if (values.count < names.count) {
+ 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 (type == NULL && values.count == 0 && names.count > 0) {
+ syntax_error(f->curr_token, "Missing type or initialization");
+ return make_bad_decl(f, f->curr_token, f->curr_token);
+ }
+
+ // TODO(bill): Fix this so it does not require it
+ if (values.e == NULL) {
+ values = make_ast_node_array(f);
+ }
+
+ return make_value_spec(f, keyword, names, type, values);
+}
+
+
+AstNode *parse_type_decl(AstFile *f) {
+ Token token = expect_token(f, Token_type);
+ AstNode *name = parse_identifier(f);
+ AstNode *type = parse_type(f);
+ return make_type_decl(f, token, name, type);
+}
+
+AstNode *parse_proc_decl(AstFile *f);
+
+AstNode *parse_decl(AstFile *f) {
+ switch (f->curr_token.kind) {
+ case Token_var:
+ case Token_const:
+ return parse_generic_decl(f, f->curr_token.kind, parse_value_spec);
+
+ case Token_type:
+ return parse_type_decl(f);
+
+ case Token_proc:
+ return parse_proc_decl(f);
+
+ default: {
+ Token token = f->curr_token;
+ syntax_error(token, "Expected a declaration");
+ fix_advance_to_next_stmt(f);
+ return make_bad_decl(f, token, f->curr_token);
+ }
+ }
+}
+
AstNode *parse_simple_stmt(AstFile *f) {
switch (f->curr_token.kind) {
case Token_var:
case Token_const:
- return parse_value_decl(f);
+ return parse_decl(f);
}
isize lhs_count = 0, rhs_count = 0;
@@ -1888,85 +2057,6 @@ AstNode *parse_simple_stmt(AstFile *f) {
}
return make_assign_stmt(f, token, lhs, rhs);
} break;
-
- // case Token_Colon: { // Declare
- // AstNodeArray names = lhs;
- // parse_check_name_list_for_reserves(f, names);
-
- // Token colon = expect_token(f, Token_Colon);
- // AstNode *type = parse_identifier_or_type(f);
- // AstNodeArray values = {0};
-
- // if (allow_token(f, Token_Eq)) {
- // 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 == 0) {
- // syntax_error(f->curr_token, "Expected an expression for this declaration");
- // }
- // if (type == NULL && values.count == 0) {
- // syntax_error(f->curr_token, "Missing variable type or initialization");
- // return make_bad_decl(f, f->curr_token, f->curr_token);
- // }
- // }
-
- // if (values.e == NULL) {
- // values = make_ast_node_array(f);
- // }
-
- // return make_var_decl(f, names, type, values);
- // } break;
-
- // case Token_ColonColon: {
- // AstNodeArray names = lhs;
- // parse_check_name_list_for_reserves(f, names);
-
- // Token colon_colon = expect_token(f, Token_ColonColon);
-
- // // if (f->curr_token.kind == Token_type ||
- // // f->curr_token.kind == Token_struct ||
- // // f->curr_token.kind == Token_enum ||
- // // f->curr_token.kind == Token_union ||
- // // f->curr_token.kind == Token_raw_union) {
- // // // if (f->curr_token.kind == Token_type) {
- // // Token token = f->curr_token;
- // // if (token.kind == Token_type) {
- // // next_token(f);
- // // }
- // // if (names.count != 1) {
- // // syntax_error_node(names.e[0], "You can only declare one type at a time");
- // // return make_bad_decl(f, names.e[0]->Ident, token);
- // // }
-
- // // return make_type_decl(f, token, names.e[0], parse_type(f));
- // // } else if (f->curr_token.kind == Token_proc) {
- // // // NOTE(bill): Procedure declarations
- // // Token proc_token = f->curr_token;
- // // AstNode *name = names.e[0];
- // // if (names.count != 1) {
- // // syntax_error(proc_token, "You can only declare one procedure at a time");
- // // return make_bad_decl(f, name->Ident, proc_token);
- // // }
-
- // // return parse_proc_decl(f, proc_token, name);
- // // }
-
- // AstNodeArray 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) {
- // 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 (values.count == 0 && names.count > 0) {
- // syntax_error(f->curr_token, "Missing constant value");
- // return make_bad_decl(f, f->curr_token, f->curr_token);
- // }
-
- // return make_const_decl(f, names, NULL, values);
- // } break;
}
if (lhs_count > 1) {
@@ -2013,41 +2103,6 @@ AstNode *convert_stmt_to_expr(AstFile *f, AstNode *statement, String kind) {
return make_bad_expr(f, f->curr_token, f->tokens.e[f->curr_token_index+1]);
}
-AstNodeArray parse_identfier_list(AstFile *f) {
- AstNodeArray list = make_ast_node_array(f);
-
- do {
- array_add(&list, parse_identifier(f));
- if (f->curr_token.kind != Token_Comma ||
- f->curr_token.kind == Token_EOF) {
- break;
- }
- next_token(f);
- } while (true);
-
- return list;
-}
-
-
-
-AstNode *parse_type_attempt(AstFile *f) {
- AstNode *type = parse_identifier_or_type(f);
- if (type != NULL) {
- // TODO(bill): Handle?
- }
- return type;
-}
-
-AstNode *parse_type(AstFile *f) {
- AstNode *type = parse_type_attempt(f);
- if (type == NULL) {
- Token token = f->curr_token;
- syntax_error(token, "Expected a type");
- next_token(f);
- return make_bad_expr(f, token, f->curr_token);
- }
- return type;
-}
void parse_proc_signature(AstFile *f, AstNodeArray *params, AstNodeArray *results);
@@ -2691,75 +2746,15 @@ AstNode *parse_asm_stmt(AstFile *f) {
}
-AstNode *parse_type_decl(AstFile *f) {
- Token token = expect_token(f, Token_type);
- AstNode *name = parse_identifier(f);
- AstNode *type = parse_type(f);
- return make_type_decl(f, token, name, type);
-}
-
-AstNode *parse_value_decl(AstFile *f) {
- Token token = f->curr_token;
- switch (token.kind) {
- case Token_var:
- case Token_const:
- next_token(f);
- break;
- default:
- next_token(f);
- syntax_error(token, "Expected a variable or constant declaration");
- fix_advance_to_next_stmt(f);
- return make_bad_decl(f, token, f->curr_token);
- }
-
- AstNodeArray names = parse_identfier_list(f);
- parse_check_name_list_for_reserves(f, names);
- AstNode *type = parse_type_attempt(f);
- AstNodeArray values = {0};
-
- if (allow_token(f, Token_Eq)) {
- 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 (token.kind == Token_const) {
- if (values.count < names.count) {
- 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 (type == NULL && values.count == 0 && names.count > 0) {
- syntax_error(f->curr_token, "Missing type or initialization");
- return make_bad_decl(f, f->curr_token, f->curr_token);
- }
-
- // TODO(bill): Fix this so it does not require it
- if (values.e == NULL) {
- values = make_ast_node_array(f);
- }
-
-
- AstNode *decl = NULL;
-
- switch (token.kind) {
- case Token_var:
- decl = make_var_decl(f, names, type, values);
- break;
- case Token_const:
- decl = make_const_decl(f, names, type, values);
- break;
- }
- return decl;
-}
AstNode *parse_stmt(AstFile *f) {
AstNode *s = NULL;
Token token = f->curr_token;
switch (token.kind) {
// Operands
+ case Token_var:
+ case Token_const:
+
case Token_Ident:
case Token_Integer:
case Token_Float:
@@ -2775,6 +2770,13 @@ AstNode *parse_stmt(AstFile *f) {
expect_semicolon(f, s);
return s;
+ case Token_proc:
+ return parse_proc_decl(f);
+ case Token_type:
+ s = parse_type_decl(f);
+ expect_semicolon(f, s);
+ return s;
+
// TODO(bill): other keywords
case Token_if: return parse_if_stmt(f);
case Token_when: return parse_when_stmt(f);
@@ -2792,25 +2794,9 @@ AstNode *parse_stmt(AstFile *f) {
expect_semicolon(f, s);
return s;
- case Token_proc:
- return parse_proc_decl(f);
- case Token_type:
- s = parse_type_decl(f);
- expect_semicolon(f, s);
- return s;
- case Token_var:
- case Token_const:
- s = parse_value_decl(f);
- expect_semicolon(f, s);
- return s;
-
-
case Token_using: {
- AstNode *node = NULL;
-
next_token(f);
- node = parse_stmt(f);
-
+ AstNode *node = parse_stmt(f);
bool valid = false;
switch (node->kind) {
@@ -2833,7 +2819,6 @@ AstNode *parse_stmt(AstFile *f) {
return make_bad_stmt(f, token, f->curr_token);
}
-
return make_using_stmt(f, token, node);
} break;
@@ -2961,17 +2946,23 @@ AstNode *parse_stmt(AstFile *f) {
expect_semicolon(f, s);
return s;
} else if (str_eq(tag, str_lit("thread_local"))) {
- AstNode *var_decl = parse_simple_stmt(f);
- if (var_decl->kind != AstNode_VarDecl) {
+ AstNode *decl = parse_simple_stmt(f);
+ if (decl->kind != AstNode_VarDecl &&
+ (decl->kind == AstNode_GenericDecl &&
+ decl->GenericDecl.token.kind != Token_var)) {
syntax_error(token, "#thread_local may only be applied to variable declarations");
- return make_bad_decl(f, token, ast_node_token(var_decl));
+ return make_bad_decl(f, token, ast_node_token(decl));
}
if (f->curr_proc != NULL) {
syntax_error(token, "#thread_local is only allowed at the file scope");
- return make_bad_decl(f, token, ast_node_token(var_decl));
+ return make_bad_decl(f, token, ast_node_token(decl));
+ }
+ if (decl->kind == AstNode_VarDecl) {
+ decl->VarDecl.tags |= VarDeclTag_thread_local;
+ } else if (decl->kind == AstNode_GenericDecl) {
+ decl->GenericDecl.tags |= VarDeclTag_thread_local;
}
- var_decl->VarDecl.tags |= VarDeclTag_thread_local;
- return var_decl;
+ return decl;
} else if (str_eq(tag, str_lit("bounds_check"))) {
s = parse_stmt(f);
s->stmt_state_flags |= StmtStateFlag_bounds_check;
diff --git a/src/ssa.c b/src/ssa.c
index f4100e9cf..bc5737676 100644
--- a/src/ssa.c
+++ b/src/ssa.c
@@ -3849,7 +3849,8 @@ void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node) {
case_ast_node(us, UsingStmt, node);
AstNode *decl = unparen_expr(us->node);
- if (decl->kind == AstNode_VarDecl) {
+ if (decl->kind == AstNode_VarDecl &&
+ decl->kind == AstNode_GenericDecl) {
ssa_build_stmt(proc, decl);
}
case_end;
@@ -3858,6 +3859,75 @@ void ssa_build_stmt_internal(ssaProcedure *proc, AstNode *node) {
ssa_build_when_stmt(proc, ws);
case_end;
+
+ case_ast_node(gd, GenericDecl, node);
+ for_array(spec_index, gd->specs) {
+ AstNode *spec = gd->specs.e[spec_index];
+ switch (spec->kind) {
+ case_ast_node(vs, ValueSpec, spec);
+ switch (vs->keyword) {
+ case Token_const:
+ break;
+ case Token_var: {
+ ssaModule *m = proc->module;
+ gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);
+
+ if (vs->values.count == 0) { // declared and zero-initialized
+ for_array(i, vs->names) {
+ AstNode *name = vs->names.e[i];
+ if (!ssa_is_blank_ident(name)) {
+ ssa_add_local_for_identifier(proc, name, true);
+ }
+ }
+ } else { // Tuple(s)
+ Array(ssaAddr) lvals;
+ ssaValueArray inits;
+ array_init_reserve(&lvals, m->tmp_allocator, vs->names.count);
+ array_init_reserve(&inits, m->tmp_allocator, vs->names.count);
+
+ for_array(i, vs->names) {
+ AstNode *name = vs->names.e[i];
+ ssaAddr lval = ssa_make_addr(NULL, NULL);
+ if (!ssa_is_blank_ident(name)) {
+ ssa_add_local_for_identifier(proc, name, false);
+ lval = ssa_build_addr(proc, name);
+ }
+
+ array_add(&lvals, lval);
+ }
+
+ for_array(i, vs->values) {
+ ssaValue *init = ssa_build_expr(proc, vs->values.e[i]);
+ Type *t = ssa_type(init);
+ if (t->kind == Type_Tuple) {
+ for (isize i = 0; i < t->Tuple.variable_count; i++) {
+ Entity *e = t->Tuple.variables[i];
+ ssaValue *v = ssa_emit_struct_ev(proc, init, i);
+ array_add(&inits, v);
+ }
+ } else {
+ array_add(&inits, init);
+ }
+ }
+
+
+ for_array(i, inits) {
+ if (lvals.e[i].addr == NULL) {
+ continue;
+ }
+ ssaValue *v = ssa_emit_conv(proc, inits.e[i], ssa_addr_type(lvals.e[i]));
+ ssa_addr_store(proc, lvals.e[i], v);
+ }
+ }
+
+ gb_temp_arena_memory_end(tmp);
+ } break;
+ }
+ case_end;
+ }
+ }
+ case_end;
+
case_ast_node(vd, VarDecl, node);
ssaModule *m = proc->module;
gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&m->tmp_arena);