From d9c686b53d63023c432293fa906d96224cc36a23 Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Tue, 29 Nov 2016 23:57:06 +0000 Subject: [PATCH] `when` statement; Better entity collection system (for both local and global); Better parsing for record declarations --- code/demo.odin | 29 ++--- core/opengl.odin | 4 +- core/os.odin | 4 +- core/win32.odin | 8 +- src/checker/checker.c | 39 +++--- src/checker/expr.c | 294 +++++++++++++++++++++++++++++------------- src/checker/stmt.c | 68 +--------- src/checker/types.c | 17 +-- src/parser.c | 111 ++++++++-------- 9 files changed, 309 insertions(+), 265 deletions(-) diff --git a/code/demo.odin b/code/demo.odin index 303c3b448..455dadb46 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,29 +1,18 @@ -// #import "fmt.odin" +#import "fmt.odin" #import "utf8.odin" -when ODIN_OS == "window" { - when ODIN_OS != "window" { - } else { - MAX :: 64 - } - #import "fmt.odin" -} else { - -} - - main :: proc() { - when true { - OffsetType :: type int - } - // MAX :: 64 buf: [MAX]rune backing: [MAX]byte - offset: OffsetType + offset: int + when MAX > 0 { + msg := "Hello" + } + + MAX :: 64 - msg := "Hello" count := utf8.rune_count(msg) assert(count <= MAX) runes := buf[:count] @@ -33,14 +22,14 @@ main :: proc() { s := msg[offset:] r, len := utf8.decode_rune(s) runes[count-i-1] = r - offset += len as OffsetType + offset += len } offset = 0 for i := 0; i < count; i++ { data, len := utf8.encode_rune(runes[i]) copy(backing[offset:], data[:len]) - offset += len as OffsetType + offset += len } reverse := backing[:offset] as string diff --git a/core/opengl.odin b/core/opengl.odin index 94d943809..0d015469d 100644 --- a/core/opengl.odin +++ b/core/opengl.odin @@ -1,4 +1,6 @@ -#foreign_system_library "opengl32" +when ODIN_OS == "windows" { + #foreign_system_library "opengl32" +} #import "win32.odin" #load "opengl_constants.odin" diff --git a/core/os.odin b/core/os.odin index 1286065bc..6ed03a6d1 100644 --- a/core/os.odin +++ b/core/os.odin @@ -1,4 +1,6 @@ -#import "win32.odin" +when ODIN_OS == "windows" { + #import "win32.odin" +} #import "fmt.odin" File_Time :: type u64 diff --git a/core/win32.odin b/core/win32.odin index 540990b90..195ddca8f 100644 --- a/core/win32.odin +++ b/core/win32.odin @@ -1,7 +1,7 @@ -#foreign_system_library "user32" -#foreign_system_library "gdi32" - -_:= compile_assert(ODIN_OS == "windows") +when ODIN_OS == "windows" { + #foreign_system_library "user32" + #foreign_system_library "gdi32" +} HANDLE :: type rawptr HWND :: type HANDLE diff --git a/src/checker/checker.c b/src/checker/checker.c index a5e77be35..cdd6be158 100644 --- a/src/checker/checker.c +++ b/src/checker/checker.c @@ -917,10 +917,12 @@ Type *const curr_procedure(Checker *c) { } void add_curr_ast_file(Checker *c, AstFile *file) { - TokenPos zero_pos = {0}; - global_error_collector.prev = zero_pos; - c->curr_ast_file = file; - c->context.decl = file->decl_info; + if (file != NULL) { + TokenPos zero_pos = {0}; + global_error_collector.prev = zero_pos; + c->curr_ast_file = file; + c->context.decl = file->decl_info; + } } @@ -966,9 +968,6 @@ MapEntity generate_minimum_dependency_map(CheckerInfo *info, Entity *start) { return map; } -void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes, MapScope *file_scopes); - - #include "expr.c" #include "decl.c" @@ -1072,6 +1071,7 @@ void check_global_entities_by_kind(Checker *c, EntityKind kind) { } } +void check_global_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes, MapScope *file_scopes); void check_global_when_stmt(Checker *c, Scope *parent_scope, AstNodeWhenStmt *ws, MapScope *file_scopes) { Operand operand = {Addressing_Invalid}; @@ -1088,11 +1088,11 @@ void check_global_when_stmt(Checker *c, Scope *parent_scope, AstNodeWhenStmt *ws if (operand.value.kind == ExactValue_Bool && operand.value.value_bool == true) { ast_node(body, BlockStmt, ws->body); - check_collect_entities(c, parent_scope, body->stmts, file_scopes); + check_global_collect_entities(c, parent_scope, body->stmts, file_scopes); } else if (ws->else_stmt) { switch (ws->else_stmt->kind) { case AstNode_BlockStmt: - check_collect_entities(c, parent_scope, ws->else_stmt->BlockStmt.stmts, file_scopes); + check_global_collect_entities(c, parent_scope, ws->else_stmt->BlockStmt.stmts, file_scopes); break; case AstNode_WhenStmt: check_global_when_stmt(c, parent_scope, &ws->else_stmt->WhenStmt, file_scopes); @@ -1104,7 +1104,7 @@ void check_global_when_stmt(Checker *c, Scope *parent_scope, AstNodeWhenStmt *ws } } } -void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes, MapScope *file_scopes) { +void check_global_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes, MapScope *file_scopes) { for_array(decl_index, nodes) { AstNode *decl = nodes.e[decl_index]; if (!is_ast_node_decl(decl) && !is_ast_node_when_stmt(decl)) { @@ -1242,9 +1242,8 @@ void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes, try_add_foreign_library_path(c, file_str); case_end; case_ast_node(ws, WhenStmt, decl); - check_global_when_stmt(c, parent_scope, ws, file_scopes); + // Will be handled later case_end; - case_ast_node(cd, ConstDecl, decl); for_array(i, cd->values) { AstNode *name = cd->names.e[i]; @@ -1267,7 +1266,6 @@ void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes, error(ast_node_token(decl), "Extra initial expression"); } case_end; - case_ast_node(vd, VarDecl, decl); if (!parent_scope->is_file) { // NOTE(bill): Within a procedure, variables must be in order @@ -1308,7 +1306,6 @@ void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes, add_entity_and_decl_info(c, name, e, d); } case_end; - case_ast_node(td, TypeDecl, decl); ast_node(n, Ident, td->name); Entity *e = make_entity_type_name(c->allocator, parent_scope, *n, NULL); @@ -1317,7 +1314,6 @@ void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes, d->type_expr = td->type; add_entity_and_decl_info(c, td->name, e, d); case_end; - case_ast_node(pd, ProcDecl, decl); ast_node(n, Ident, pd->name); Token token = *n; @@ -1335,6 +1331,17 @@ void check_collect_entities(Checker *c, Scope *parent_scope, AstNodeArray nodes, break; } } + + // NOTE(bill): `when` stmts need to be handled after the other as the condition may refer to something + // declared after this stmt in source + for_array(decl_index, nodes) { + AstNode *decl = nodes.e[decl_index]; + switch (decl->kind) { + case_ast_node(ws, WhenStmt, decl); + check_global_when_stmt(c, parent_scope, ws, file_scopes); + case_end; + } + } } @@ -1371,7 +1378,7 @@ void check_parsed_files(Checker *c) { for_array(i, c->parser->files) { AstFile *f = &c->parser->files.e[i]; add_curr_ast_file(c, f); - check_collect_entities(c, f->scope, f->decls, &file_scopes); + check_global_collect_entities(c, f->scope, f->decls, &file_scopes); } check_global_entities_by_kind(c, Entity_TypeName); diff --git a/src/checker/expr.c b/src/checker/expr.c index 9b78c0811..ba92454ec 100644 --- a/src/checker/expr.c +++ b/src/checker/expr.c @@ -20,6 +20,206 @@ gb_inline Type *check_type(Checker *c, AstNode *expression) { + +typedef struct DelayedEntity { + Entity *entity; + DeclInfo *decl; +} DelayedEntity; + +typedef struct DelayedOtherFields { + Entity **other_fields; + isize other_field_count; + isize other_field_index; + + MapEntity *entity_map; +} DelayedOtherFields; + +typedef Array(DelayedEntity) DelayedEntities; + +void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntities *delayed_entities, DelayedOtherFields *dof); + +void check_local_collect_entities_from_when_stmt(Checker *c, AstNodeWhenStmt *ws, DelayedEntities *delayed_entities, DelayedOtherFields *dof) { + Operand operand = {Addressing_Invalid}; + check_expr(c, &operand, ws->cond); + if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) { + error(ast_node_token(ws->cond), "Non-boolean condition in `when` statement"); + } + if (operand.mode != Addressing_Constant) { + error(ast_node_token(ws->cond), "Non-constant condition in `when` statement"); + } + if (ws->body == NULL || ws->body->kind != AstNode_BlockStmt) { + error(ast_node_token(ws->cond), "Invalid body for `when` statement"); + } else { + if (operand.value.kind == ExactValue_Bool && + operand.value.value_bool) { + check_local_collect_entities(c, ws->body->BlockStmt.stmts, delayed_entities, dof); + } else if (ws->else_stmt) { + switch (ws->else_stmt->kind) { + case AstNode_BlockStmt: + check_local_collect_entities(c, ws->else_stmt->BlockStmt.stmts, delayed_entities, dof); + break; + case AstNode_WhenStmt: + check_local_collect_entities_from_when_stmt(c, &ws->else_stmt->WhenStmt, delayed_entities, dof); + break; + default: + error(ast_node_token(ws->else_stmt), "Invalid `else` statement in `when` statement"); + break; + } + } + } +} + +// NOTE(bill): The `dof` is for use within records +void check_local_collect_entities(Checker *c, AstNodeArray nodes, DelayedEntities *delayed_entities, DelayedOtherFields *dof) { + for_array(i, nodes) { + AstNode *node = nodes.e[i]; + switch (node->kind) { + case_ast_node(ws, WhenStmt, node); + // Will be handled later + case_end; + case_ast_node(cd, ConstDecl, node); + gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); + + isize entity_count = cd->names.count; + isize entity_index = 0; + Entity **entities = gb_alloc_array(c->tmp_allocator, Entity *, entity_count); + + for_array(i, cd->values) { + AstNode *name = cd->names.e[i]; + AstNode *value = cd->values.e[i]; + 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 = cd->type; + d->init_expr = value; + + add_entity_and_decl_info(c, name, e, d); + + DelayedEntity delay = {e, d}; + array_add(delayed_entities, delay); + } + + isize lhs_count = cd->names.count; + isize rhs_count = cd->values.count; + + // TODO(bill): Better error messages or is this good enough? + if (rhs_count == 0 && cd->type == NULL) { + error(ast_node_token(node), "Missing type or initial expression"); + } else if (lhs_count < rhs_count) { + error(ast_node_token(node), "Extra initial expression"); + } + + if (dof != NULL) { + // NOTE(bill): Within a record + for_array(i, cd->names) { + AstNode *name = cd->names.e[i]; + Entity *e = entities[i]; + 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); + case_end; + + case_ast_node(pd, ProcDecl, node); + // NOTE(bill): This must be handled here so it has access to the parent scope stuff + // e.g. using + Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->Ident, NULL); + e->identifier = pd->name; + + DeclInfo *d = make_declaration_info(c->allocator, e->scope); + d->proc_decl = node; + + add_entity_and_decl_info(c, pd->name, e, d); + check_entity_decl(c, e, d, NULL, NULL); + case_end; + + case_ast_node(td, TypeDecl, node); + Token name_token = td->name->Ident; + + Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, NULL); + e->identifier = td->name; + + DeclInfo *d = make_declaration_info(c->allocator, e->scope); + d->type_expr = td->type; + + add_entity_and_decl_info(c, td->name, e, d); + + DelayedEntity delay = {e, d}; + array_add(delayed_entities, delay); + + + if (dof != NULL) { + 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, td->name, e); + add_entity_use(c, td->name, e); + } + } + case_end; + } + } + + // NOTE(bill): `when` stmts need to be handled after the other as the condition may refer to something + // declared after this stmt in source + for_array(i, nodes) { + AstNode *node = nodes.e[i]; + switch (node->kind) { + case_ast_node(ws, WhenStmt, node); + check_local_collect_entities_from_when_stmt(c, ws, delayed_entities, dof); + case_end; + } + } +} + +void check_scope_decls(Checker *c, AstNodeArray nodes, isize reserve_size, DelayedOtherFields *dof) { + DelayedEntities delayed_entities; + array_init_reserve(&delayed_entities, heap_allocator(), reserve_size); + check_local_collect_entities(c, nodes, &delayed_entities, dof); + + for_array(i, delayed_entities) { + DelayedEntity delayed = delayed_entities.e[i]; + if (delayed.entity->kind == Entity_TypeName) { + check_entity_decl(c, delayed.entity, delayed.decl, NULL, NULL); + } + } + for_array(i, delayed_entities) { + DelayedEntity delayed = delayed_entities.e[i]; + if (delayed.entity->kind == Entity_Constant) { + check_entity_decl(c, delayed.entity, delayed.decl, NULL, NULL); + } + } + + array_free(&delayed_entities); +} + + bool check_is_assignable_to_using_subtype(Type *dst, Type *src) { Type *prev_src = src; // Type *prev_dst = dst; @@ -247,96 +447,12 @@ void check_fields(Checker *c, AstNode *node, AstNodeArray decls, isize other_field_index = 0; Entity *using_index_expr = NULL; + DelayedOtherFields dof = {0}; + dof.other_fields = other_fields; + dof.other_field_count = other_field_count; + dof.entity_map = &entity_map; - typedef struct { - Entity *e; - AstNode *t; - } Delay; - Array(Delay) delayed_const; array_init_reserve(&delayed_const, c->tmp_allocator, other_field_count); - Array(Delay) delayed_type; array_init_reserve(&delayed_type, c->tmp_allocator, other_field_count); - - for_array(decl_index, decls) { - AstNode *decl = decls.e[decl_index]; - if (decl->kind == AstNode_ConstDecl) { - ast_node(cd, ConstDecl, decl); - - isize entity_count = cd->names.count; - isize entity_index = 0; - Entity **entities = gb_alloc_array(c->allocator, Entity *, entity_count); - - for_array(i, cd->values) { - AstNode *name = cd->names.e[i]; - AstNode *value = cd->values.e[i]; - - GB_ASSERT(name->kind == AstNode_Ident); - ExactValue v = {ExactValue_Invalid}; - Token name_token = name->Ident; - Entity *e = make_entity_constant(c->allocator, c->context.scope, name_token, NULL, v); - entities[entity_index++] = e; - - Delay delay = {e, cd->type}; - array_add(&delayed_const, delay); - } - - isize lhs_count = cd->names.count; - isize rhs_count = cd->values.count; - - // TODO(bill): Better error messages or is this good enough? - if (rhs_count == 0 && cd->type == NULL) { - error(ast_node_token(node), "Missing type or initial expression"); - } else if (lhs_count < rhs_count) { - error(ast_node_token(node), "Extra initial expression"); - } - - for_array(i, cd->names) { - AstNode *name = cd->names.e[i]; - Entity *e = entities[i]; - Token name_token = name->Ident; - if (str_eq(name_token.string, str_lit("_"))) { - other_fields[other_field_index++] = e; - } else { - HashKey key = hash_string(name_token.string); - if (map_entity_get(&entity_map, key) != NULL) { - // TODO(bill): Scope checking already checks the declaration - error(name_token, "`%.*s` is already declared in this structure", LIT(name_token.string)); - } else { - map_entity_set(&entity_map, key, e); - other_fields[other_field_index++] = e; - } - add_entity(c, c->context.scope, name, e); - } - } - } else if (decl->kind == AstNode_TypeDecl) { - ast_node(td, TypeDecl, decl); - Token name_token = td->name->Ident; - - Entity *e = make_entity_type_name(c->allocator, c->context.scope, name_token, NULL); - Delay delay = {e, td->type}; - array_add(&delayed_type, delay); - - if (str_eq(name_token.string, str_lit("_"))) { - other_fields[other_field_index++] = e; - } else { - HashKey key = hash_string(name_token.string); - if (map_entity_get(&entity_map, key) != NULL) { - // TODO(bill): Scope checking already checks the declaration - error(name_token, "`%.*s` is already declared in this structure", LIT(name_token.string)); - } else { - map_entity_set(&entity_map, key, e); - other_fields[other_field_index++] = e; - } - add_entity(c, c->context.scope, td->name, e); - add_entity_use(c, td->name, e); - } - } - } - - for_array(i, delayed_type) { - check_const_decl(c, delayed_type.e[i].e, delayed_type.e[i].t, NULL); - } - for_array(i, delayed_const) { - check_type_decl(c, delayed_const.e[i].e, delayed_const.e[i].t, NULL, NULL); - } + check_scope_decls(c, decls, 1.2*other_field_count, &dof); if (node->kind == AstNode_UnionType) { isize field_index = 0; diff --git a/src/checker/stmt.c b/src/checker/stmt.c index d483236ac..f0666e9a6 100644 --- a/src/checker/stmt.c +++ b/src/checker/stmt.c @@ -11,74 +11,12 @@ typedef enum StmtFlag { } StmtFlag; - void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) { if (stmts.count == 0) { return; } - gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&c->tmp_arena); - - typedef struct { - Entity *e; - DeclInfo *d; - } Delay; - Array(Delay) delayed_const; array_init_reserve(&delayed_const, c->tmp_allocator, stmts.count); - Array(Delay) delayed_type; array_init_reserve(&delayed_type, c->tmp_allocator, stmts.count); - - for_array(i, stmts) { - AstNode *node = stmts.e[i]; - switch (node->kind) { - case_ast_node(cd, ConstDecl, node); - for_array(i, cd->values) { - AstNode *name = cd->names.e[i]; - AstNode *value = cd->values.e[i]; - ExactValue v = {ExactValue_Invalid}; - - Entity *e = make_entity_constant(c->allocator, c->context.scope, name->Ident, NULL, v); - e->identifier = name; - - DeclInfo *d = make_declaration_info(c->allocator, e->scope); - d->type_expr = cd->type; - d->init_expr = value; - - add_entity_and_decl_info(c, name, e, d); - - Delay delay = {e, d}; - array_add(&delayed_const, delay); - } - - isize lhs_count = cd->names.count; - isize rhs_count = cd->values.count; - - if (rhs_count == 0 && cd->type == NULL) { - error(ast_node_token(node), "Missing type or initial expression"); - } else if (lhs_count < rhs_count) { - error(ast_node_token(node), "Extra initial expression"); - } - case_end; - - case_ast_node(td, TypeDecl, node); - Entity *e = make_entity_type_name(c->allocator, c->context.scope, td->name->Ident, NULL); - e->identifier = td->name; - - DeclInfo *d = make_declaration_info(c->allocator, e->scope); - d->type_expr = td->type; - - add_entity_and_decl_info(c, td->name, e, d); - - Delay delay = {e, d}; - array_add(&delayed_type, delay); - case_end; - } - } - - for_array(i, delayed_type) { - check_entity_decl(c, delayed_type.e[i].e, delayed_type.e[i].d, NULL, NULL); - } - for_array(i, delayed_const) { - check_entity_decl(c, delayed_const.e[i].e, delayed_const.e[i].d, NULL, NULL); - } + check_scope_decls(c, stmts, 1.2*stmts.count, NULL); bool ft_ok = (flags & Stmt_FallthroughAllowed) != 0; u32 f = flags & (~Stmt_FallthroughAllowed); @@ -95,7 +33,6 @@ void check_stmt_list(Checker *c, AstNodeArray stmts, u32 flags) { check_stmt(c, n, new_flags); } - gb_temp_arena_memory_end(tmp); } bool check_is_terminating_list(AstNodeArray stmts) { @@ -1147,6 +1084,8 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { case_end; case_ast_node(pd, ProcDecl, node); + // NOTE(bill): Handled elsewhere + #if 0 // NOTE(bill): This must be handled here so it has access to the parent scope stuff // e.g. using Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->Ident, NULL); @@ -1157,6 +1096,7 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { add_entity_and_decl_info(c, pd->name, e, d); check_entity_decl(c, e, d, NULL, NULL); + #endif case_end; } } diff --git a/src/checker/types.c b/src/checker/types.c index 72ce813ea..989789190 100644 --- a/src/checker/types.c +++ b/src/checker/types.c @@ -667,24 +667,9 @@ bool is_type_comparable(Type *t) { t = base_type(get_enum_base_type(t)); switch (t->kind) { case Type_Basic: - return t->kind != Basic_UntypedNil; + return t->kind != Basic_UntypedNil && t->kind != Basic_any; case Type_Pointer: return true; - case Type_Record: { - if (false && is_type_struct(t)) { - // TODO(bill): Should I even allow this? - for (isize i = 0; i < t->Record.field_count; i++) { - if (!is_type_comparable(t->Record.fields[i]->type)) - return false; - } - } else if (is_type_enum(t)) { - return is_type_comparable(t->Record.enum_base); - } - return false; - } break; - case Type_Array: - return false; - // return is_type_comparable(t->Array.elem); case Type_Vector: return is_type_comparable(t->Vector.elem); case Type_Proc: diff --git a/src/parser.c b/src/parser.c index e2b046a69..f46bae4da 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1086,6 +1086,7 @@ void fix_advance_to_next_stmt(AstFile *f) { return; case Token_if: + case Token_when: case Token_return: case Token_for: case Token_match: @@ -1966,55 +1967,61 @@ AstNodeArray parse_parameter_list(AstFile *f) { } -AstNodeArray parse_struct_params(AstFile *f, isize *decl_count_, bool using_allowed) { +AstNodeArray parse_record_params(AstFile *f, isize *decl_count_, bool using_allowed, String context) { AstNodeArray decls = make_ast_node_array(f); isize decl_count = 0; - while (f->curr_token.kind == Token_Identifier || - f->curr_token.kind == Token_using) { - bool is_using = false; - if (allow_token(f, Token_using)) { - is_using = true; - } - AstNodeArray names = parse_lhs_expr_list(f); - if (names.count == 0) { - syntax_error(f->curr_token, "Empty field declaration"); - } + while (f->curr_token.kind != Token_CloseBrace && + f->curr_token.kind != Token_EOF) { + AstNode *decl = parse_stmt(f); + if (is_ast_node_decl(decl) || + decl->kind == AstNode_UsingStmt || + decl->kind == AstNode_EmptyStmt) { + switch (decl->kind) { + case AstNode_EmptyStmt: + break; - if (!using_allowed && is_using) { - syntax_error(f->curr_token, "Cannot apply `using` to members of a union"); - is_using = false; - } - if (names.count > 1 && is_using) { - syntax_error(f->curr_token, "Cannot apply `using` to more than one of the same type"); - } + case AstNode_ProcDecl: + syntax_error(f->curr_token, "Procedure declarations are not allowed within a %.*s", LIT(context)); + break; - AstNode *decl = NULL; + case AstNode_UsingStmt: { + bool is_using = true; + if (!using_allowed) { + syntax_error(f->curr_token, "Cannot apply `using` to members of a %.*s", LIT(context)); + is_using = false; + } + if (decl->UsingStmt.node->kind == AstNode_VarDecl) { + AstNode *vd = decl->UsingStmt.node; + vd->VarDecl.is_using = is_using && using_allowed; + if (vd->VarDecl.values.count > 0) { + syntax_error(f->curr_token, "Default variable assignments within a %.*s will be ignored", LIT(context)); + } + array_add(&decls, vd); + } else { + syntax_error(ast_node_token(decl), "Illegal %.*s field", LIT(context)); + } + } break; - if (f->curr_token.kind == Token_Colon) { - decl = parse_decl(f, names); + case AstNode_VarDecl: { + if (decl->VarDecl.values.count > 0) { + syntax_error(f->curr_token, "Default variable assignments within a %.*s will be ignored", LIT(context)); + } + array_add(&decls, decl); + } - if (decl->kind == AstNode_ProcDecl) { - syntax_error(f->curr_token, "Procedure declarations are not allowed within a structure"); - decl = make_bad_decl(f, ast_node_token(names.e[0]), f->curr_token); + case AstNode_BadDecl: + break; + + case AstNode_ConstDecl: + case AstNode_TypeDecl: + default: + decl_count += 1; + array_add(&decls, decl); + break; } } else { - syntax_error(f->curr_token, "Illegal structure field"); - decl = make_bad_decl(f, ast_node_token(names.e[0]), f->curr_token); - } - - expect_semicolon_after_stmt(f, decl); - - if (is_ast_node_decl(decl)) { - array_add(&decls, decl); - if (decl->kind == AstNode_VarDecl) { - decl->VarDecl.is_using = is_using && using_allowed; - if (decl->VarDecl.values.count > 0) { - syntax_error(f->curr_token, "Default variable assignments within a structure will be ignored (at the moment)"); - } - } else { - decl_count += 1; - } + syntax_error(ast_node_token(decl), "Illegal record field: %.*s", LIT(ast_node_strings[decl->kind])); } } @@ -2063,7 +2070,11 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) { next_token(f); } else if (f->curr_token.kind == Token_vector) { next_token(f); - count_expr = parse_expr(f, false); + if (f->curr_token.kind != Token_CloseBracket) { + count_expr = parse_expr(f, false); + } else { + syntax_error(f->curr_token, "Vector type missing count"); + } is_vector = true; } else if (f->curr_token.kind != Token_CloseBracket) { count_expr = parse_expr(f, false); @@ -2076,15 +2087,6 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) { return make_array_type(f, token, count_expr, parse_type(f)); } - // case Token_OpenBrace: { - // f->expr_level++; - // Token token = expect_token(f, Token_OpenBrace); - // AstNode *count_expr = parse_expr(f, false); - // expect_token(f, Token_CloseBrace); - // f->expr_level--; - // return make_vector_type(f, token, count_expr, parse_type(f)); - // } - case Token_struct: { Token token = expect_token(f, Token_struct); bool is_packed = false; @@ -2112,7 +2114,7 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) { Token open = expect_token_after(f, Token_OpenBrace, "`struct`"); isize decl_count = 0; - AstNodeArray decls = parse_struct_params(f, &decl_count, true); + AstNodeArray decls = parse_record_params(f, &decl_count, true, str_lit("struct")); Token close = expect_token(f, Token_CloseBrace); return make_struct_type(f, token, decls, decl_count, is_packed, is_ordered); @@ -2122,7 +2124,7 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) { Token token = expect_token(f, Token_union); Token open = expect_token_after(f, Token_OpenBrace, "`union`"); isize decl_count = 0; - AstNodeArray decls = parse_struct_params(f, &decl_count, false); + AstNodeArray decls = parse_record_params(f, &decl_count, false, str_lit("union")); Token close = expect_token(f, Token_CloseBrace); return make_union_type(f, token, decls, decl_count); @@ -2132,7 +2134,7 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) { Token token = expect_token(f, Token_raw_union); Token open = expect_token_after(f, Token_OpenBrace, "`raw_union`"); isize decl_count = 0; - AstNodeArray decls = parse_struct_params(f, &decl_count, true); + AstNodeArray decls = parse_record_params(f, &decl_count, true, str_lit("raw_union")); Token close = expect_token(f, Token_CloseBrace); return make_raw_union_type(f, token, decls, decl_count); @@ -2192,8 +2194,9 @@ AstNode *parse_identifier_or_type(AstFile *f, u32 flags) { break; case Token_Eq: - if (f->prev_token.kind == Token_Colon) + if (f->prev_token.kind == Token_Colon) { break; + } // fallthrough default: syntax_error(f->curr_token,