From 3fe7fc344d7d17a571a01e531db4a0e5ff057c9f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 23 Jul 2016 11:41:11 +0100 Subject: [PATCH] Compound literals and Warnings --- examples/test.odin | 33 ++--- src/checker/checker.cpp | 72 ++++------ src/checker/expression.cpp | 285 +++++++++++++++++++++++++------------ src/checker/statements.cpp | 225 +++++++++++++++-------------- src/main.cpp | 3 + src/parser.cpp | 137 ++++++++++++++++-- src/printer.cpp | 14 ++ src/tokenizer.cpp | 66 +++++++-- 8 files changed, 539 insertions(+), 296 deletions(-) diff --git a/examples/test.odin b/examples/test.odin index 2dd012681..8d23dbaa3 100644 --- a/examples/test.odin +++ b/examples/test.odin @@ -1,27 +1,10 @@ -// import "other" - -TAU :: 6.28; -PI :: PI/2; - -type AddProc: proc(a, b: int) -> int; - - -do_thing :: proc(p: AddProc) { - p(1, 2); -} - -add :: proc(a, b: int) -> int { - return a + b; -} - - main :: proc() { - x : int = 2; - x = x * 3; - - // do_thing(add(1, x)); - do_thing(proc(a, b: int) -> f32 { - return a*b - a%b; - }); - + type Vec2: struct { x, y: f32; } + v := Vec2{1, 1}; + a := [2]int{1, 2}; // Array 2 of int + s := []int{1, 2}; // Slice of int + _, _ = a, s; + // Equivalent to + // sa := [2]int{1, 2}; s := sa[:]; + v.x = 1; } diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index 04c6fdb42..2877d1b48 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -151,12 +151,11 @@ gb_global BuiltinProcedure builtin_procedures[BuiltinProcedure_Count] = { {STR_LIT("println"), 1, true, Expression_Statement}, }; -struct ProcedureContext { +struct CheckerContext { Scope *scope; DeclarationInfo *decl; }; - struct Checker { Parser * parser; Map types; // Key: AstNode * | Expression -> Type (and value) @@ -174,17 +173,14 @@ struct Checker { gbArena arena; gbAllocator allocator; - ProcedureContext proc_context; + CheckerContext context; gbArray(Type *) procedure_stack; b32 in_defer; // TODO(bill): Actually handle correctly - isize error_prev_line; - isize error_prev_column; - isize error_count; + ErrorCollector error_collector; }; - gb_global Scope *universal_scope = NULL; @@ -199,9 +195,20 @@ Scope *make_scope(Scope *parent, gbAllocator allocator) { } void destroy_scope(Scope *scope) { + isize element_count = gb_array_count(scope->elements.entries); + for (isize i = 0; i < element_count; i++) { + Entity *e =scope->elements.entries[i].value; + if (e->kind == Entity_Variable) { + if (!e->variable.used) { + warning(e->token, "Unused variable `%.*s`", LIT(e->token.string)); + } + } + } + for (Scope *child = scope->first_child; child != NULL; child = child->next) { destroy_scope(child); } + map_destroy(&scope->elements); // NOTE(bill): No need to free scope as it "should" be allocated in an arena (except for the global scope) } @@ -254,10 +261,10 @@ void add_dependency(DeclarationInfo *d, Entity *e) { } void add_declaration_dependency(Checker *c, Entity *e) { - if (c->proc_context.decl) { + if (c->context.decl) { auto found = map_get(&c->entities, hash_pointer(e)); if (found) { - add_dependency(c->proc_context.decl, e); + add_dependency(c->context.decl, e); } } } @@ -349,7 +356,7 @@ void init_checker(Checker *c, Parser *parser) { c->allocator = gb_arena_allocator(&c->arena); c->global_scope = make_scope(universal_scope, c->allocator); - c->proc_context.scope = c->global_scope; + c->context.scope = c->global_scope; } void destroy_checker(Checker *c) { @@ -367,32 +374,6 @@ void destroy_checker(Checker *c) { } - -#define checker_err(p, token, fmt, ...) checker_err_(p, __FUNCTION__, token, fmt, ##__VA_ARGS__) -void checker_err_(Checker *c, char *function, Token token, char *fmt, ...) { - - - // NOTE(bill): Duplicate error, skip it - if (!(c->error_prev_line == token.line && c->error_prev_column == token.column)) { - c->error_prev_line = token.line; - c->error_prev_column = token.column; - - #if 0 - gb_printf_err("%s()\n", function); - #endif - - va_list va; - va_start(va, fmt); - gb_printf_err("%.*s(%td:%td) %s\n", - LIT(c->curr_ast_file->tokenizer.fullpath), token.line, token.column, - gb_bprintf_va(fmt, va)); - va_end(va); - - } - c->error_count++; - // NOTE(bill): If there are too many errors, just quit -} - TypeAndValue *type_and_value_of_expression(Checker *c, AstNode *expression) { TypeAndValue *found = map_get(&c->types, hash_pointer(expression)); return found; @@ -458,7 +439,7 @@ void 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) { - checker_err(c, entity->token, "Redeclared entity in this scope: %.*s", LIT(entity->token.string)); + error(&c->error_collector, entity->token, "Redeclared entity in this scope: %.*s", LIT(entity->token.string)); return; } } @@ -504,13 +485,13 @@ void add_scope(Checker *c, AstNode *node, Scope *scope) { void check_open_scope(Checker *c, AstNode *statement) { - Scope *scope = make_scope(c->proc_context.scope, c->allocator); + Scope *scope = make_scope(c->context.scope, c->allocator); add_scope(c, statement, scope); - c->proc_context.scope = scope; + c->context.scope = scope; } void check_close_scope(Checker *c) { - c->proc_context.scope = c->proc_context.scope->parent; + c->context.scope = c->context.scope->parent; } void push_procedure(Checker *c, Type *procedure_type) { @@ -523,8 +504,7 @@ void pop_procedure(Checker *c) { void add_curr_ast_file(Checker *c, AstFile *file) { - c->error_prev_line = 0; - c->error_prev_column = 0; + gb_zero_item(&c->error_collector); c->curr_ast_file = file; } @@ -568,7 +548,7 @@ void check_parsed_files(Checker *c) { name = name->next, value = value->next) { GB_ASSERT(name->kind == AstNode_Identifier); ExactValue v = {ExactValue_Invalid}; - Entity *e = make_entity_constant(c->allocator, c->proc_context.scope, name->identifier.token, NULL, v); + Entity *e = make_entity_constant(c->allocator, c->context.scope, name->identifier.token, NULL, v); DeclarationInfo *di = make_declaration_info(c->allocator, c->global_scope); di->type_expr = vd->type_expression; di->init_expr = value; @@ -579,9 +559,9 @@ void check_parsed_files(Checker *c) { isize rhs_count = vd->value_list_count; if (rhs_count == 0 && vd->type_expression == NULL) { - checker_err(c, ast_node_token(decl), "Missing type or initial expression"); + error(&c->error_collector, ast_node_token(decl), "Missing type or initial expression"); } else if (lhs_count < rhs_count) { - checker_err(c, ast_node_token(decl), "Extra initial expression"); + error(&c->error_collector, ast_node_token(decl), "Extra initial expression"); } } break; @@ -647,7 +627,7 @@ void check_parsed_files(Checker *c) { break; default: - checker_err(c, ast_node_token(decl), "Only declarations are allowed at file scope"); + error(&c->error_collector, ast_node_token(decl), "Only declarations are allowed at file scope"); break; } } diff --git a/src/checker/expression.cpp b/src/checker/expression.cpp index 47823a6b2..1cc36cc65 100644 --- a/src/checker/expression.cpp +++ b/src/checker/expression.cpp @@ -18,7 +18,7 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { GB_ASSERT(struct_type->kind == Type_Structure); auto *st = &node->struct_type; if (st->field_count == 0) { - checker_err(c, ast_node_token(node), "Empty struct{} definition"); + error(&c->error_collector, ast_node_token(node), "Empty struct{} definition"); return; } @@ -42,11 +42,11 @@ void check_struct_type(Checker *c, Type *struct_type, AstNode *node) { GB_ASSERT(name->kind == AstNode_Identifier); Token name_token = name->identifier.token; // TODO(bill): is the curr_scope correct? - Entity *e = make_entity_field(c->allocator, c->proc_context.scope, name_token, type); + Entity *e = make_entity_field(c->allocator, c->context.scope, name_token, type); u64 key = hash_string(name_token.string); if (map_get(&entity_map, key)) { // TODO(bill): Scope checking already checks the declaration - checker_err(c, name_token, "`%.*s` is already declared in this structure", LIT(name_token.string)); + error(&c->error_collector, name_token, "`%.*s` is already declared in this structure", LIT(name_token.string)); } else { map_set(&entity_map, key, e); fields[field_index++] = e; @@ -77,8 +77,7 @@ Type *check_get_params(Checker *c, Scope *scope, AstNode *field_list, isize fiel add_entity(c, scope, name, param); variables[variable_index++] = param; } else { - checker_err(c, ast_node_token(name), - "Invalid parameter (invalid AST)"); + error(&c->error_collector, ast_node_token(name), "Invalid parameter (invalid AST)"); } } } @@ -107,7 +106,7 @@ Type *check_get_results(Checker *c, Scope *scope, AstNode *list, isize list_coun if (get_base_type(type)->kind == Type_Array) { // TODO(bill): Should I allow array's to returned? - checker_err(c, token, "You cannot return an array from a procedure"); + error(&c->error_collector, token, "You cannot return an array from a procedure"); } } tuple->tuple.variables = variables; @@ -123,10 +122,10 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) { // gb_printf("%td -> %td\n", param_count, result_count); - Type *params = check_get_params(c, c->proc_context.scope, proc_type_node->procedure_type.param_list, param_count); - Type *results = check_get_results(c, c->proc_context.scope, proc_type_node->procedure_type.results_list, result_count); + Type *params = check_get_params(c, c->context.scope, proc_type_node->procedure_type.param_list, param_count); + Type *results = check_get_results(c, c->context.scope, proc_type_node->procedure_type.results_list, result_count); - type->procedure.scope = c->proc_context.scope; + type->procedure.scope = c->context.scope; type->procedure.params = params; type->procedure.params_count = proc_type_node->procedure_type.param_count; type->procedure.results = results; @@ -138,11 +137,10 @@ void check_identifier(Checker *c, Operand *o, AstNode *n, Type *named_type) { GB_ASSERT(n->kind == AstNode_Identifier); o->mode = Addressing_Invalid; o->expression = n; - Entity *e = NULL; - scope_lookup_parent_entity(c->proc_context.scope, n->identifier.token.string, NULL, &e); + Entity *e = scope_lookup_entity(c->context.scope, n->identifier.token.string); if (e == NULL) { - checker_err(c, n->identifier.token, - "Undeclared type or identifier `%.*s`", LIT(n->identifier.token.string)); + error(&c->error_collector, n->identifier.token, + "Undeclared type or identifier `%.*s`", LIT(n->identifier.token.string)); return; } add_entity_use(c, n, e); @@ -200,7 +198,7 @@ i64 check_array_count(Checker *c, AstNode *e) { check_expression(c, &o, e); if (o.mode != Addressing_Constant) { if (o.mode != Addressing_Invalid) { - checker_err(c, ast_node_token(e), "Array count must be a constant"); + error(&c->error_collector, ast_node_token(e), "Array count must be a constant"); } return 0; } @@ -209,12 +207,12 @@ i64 check_array_count(Checker *c, AstNode *e) { i64 count = o.value.value_integer; if (count >= 0) return count; - checker_err(c, ast_node_token(e), "Invalid array count"); + error(&c->error_collector, ast_node_token(e), "Invalid array count"); return 0; } } - checker_err(c, ast_node_token(e), "Array count must be an integer"); + error(&c->error_collector, ast_node_token(e), "Array count must be an integer"); } return 0; } @@ -239,11 +237,11 @@ Type *check_type_expression_extra(Checker *c, AstNode *e, Type *named_type) { case Addressing_NoValue: err_str = expression_to_string(e); - checker_err(c, ast_node_token(e), "`%s` used as a type", err_str); + error(&c->error_collector, ast_node_token(e), "`%s` used as a type", err_str); break; default: err_str = expression_to_string(e); - checker_err(c, ast_node_token(e), "`%s` used as a type when not a type", err_str); + error(&c->error_collector, ast_node_token(e), "`%s` used as a type when not a type", err_str); break; } } break; @@ -289,7 +287,7 @@ Type *check_type_expression_extra(Checker *c, AstNode *e, Type *named_type) { default: err_str = expression_to_string(e); - checker_err(c, ast_node_token(e), "`%s` is not a type", err_str); + error(&c->error_collector, ast_node_token(e), "`%s` is not a type", err_str); break; } @@ -321,11 +319,11 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type) { case Addressing_NoValue: err_str = expression_to_string(e); - checker_err(c, ast_node_token(e), "`%s` used as a type", err_str); + error(&c->error_collector, ast_node_token(e), "`%s` used as a type", err_str); break; default: err_str = expression_to_string(e); - checker_err(c, ast_node_token(e), "`%s` used as a type when not a type", err_str); + error(&c->error_collector, ast_node_token(e), "`%s` used as a type when not a type", err_str); break; } } break; @@ -378,7 +376,7 @@ Type *check_type(Checker *c, AstNode *e, Type *named_type) { default: err_str = expression_to_string(e); - checker_err(c, ast_node_token(e), "`%s` is not a type", err_str); + error(&c->error_collector, ast_node_token(e), "`%s` is not a type", err_str); break; } @@ -401,25 +399,25 @@ b32 check_unary_op(Checker *c, Operand *o, Token op) { case Token_Sub: if (!is_type_numeric(o->type)) { str = expression_to_string(o->expression); - checker_err(c, op, "Operator `%.*s` is not allowed with `%s`", LIT(op.string), str); + error(&c->error_collector, op, "Operator `%.*s` is not allowed with `%s`", LIT(op.string), str); } break; case Token_Xor: if (!is_type_integer(o->type)) { - checker_err(c, op, "Operator `%.*s` is only allowed with integers", LIT(op.string)); + error(&c->error_collector, op, "Operator `%.*s` is only allowed with integers", LIT(op.string)); } break; case Token_Not: if (!is_type_boolean(o->type)) { str = expression_to_string(o->expression); - checker_err(c, op, "Operator `%.*s` is only allowed on boolean expression", LIT(op.string)); + error(&c->error_collector, op, "Operator `%.*s` is only allowed on boolean expression", LIT(op.string)); } break; default: - checker_err(c, op, "Unknown operator `%.*s`", LIT(op.string)); + error(&c->error_collector, op, "Unknown operator `%.*s`", LIT(op.string)); return false; } @@ -439,7 +437,7 @@ b32 check_binary_op(Checker *c, Operand *o, Token op) { case Token_MulEq: case Token_QuoEq: if (!is_type_numeric(o->type)) { - checker_err(c, op, "Operator `%.*s` is only allowed with numeric expressions", LIT(op.string)); + error(&c->error_collector, op, "Operator `%.*s` is only allowed with numeric expressions", LIT(op.string)); return false; } break; @@ -456,7 +454,7 @@ b32 check_binary_op(Checker *c, Operand *o, Token op) { case Token_XorEq: case Token_AndNotEq: if (!is_type_integer(o->type)) { - checker_err(c, op, "Operator `%.*s` is only allowed with integers", LIT(op.string)); + error(&c->error_collector, op, "Operator `%.*s` is only allowed with integers", LIT(op.string)); return false; } break; @@ -467,13 +465,13 @@ b32 check_binary_op(Checker *c, Operand *o, Token op) { case Token_CmpAndEq: case Token_CmpOrEq: if (!is_type_boolean(o->type)) { - checker_err(c, op, "Operator `%.*s` is only allowed with boolean expressions", LIT(op.string)); + error(&c->error_collector, op, "Operator `%.*s` is only allowed with boolean expressions", LIT(op.string)); return false; } break; default: - checker_err(c, op, "Unknown operator `%.*s`", LIT(op.string)); + error(&c->error_collector, op, "Unknown operator `%.*s`", LIT(op.string)); return false; } @@ -559,12 +557,12 @@ void check_is_expressible(Checker *c, Operand *o, Type *type) { defer (gb_string_free(b)); if (is_type_numeric(o->type) && is_type_numeric(type)) { if (!is_type_integer(o->type) && is_type_integer(type)) { - checker_err(c, ast_node_token(o->expression), "`%s` truncated to `%s`", a, b); + error(&c->error_collector, ast_node_token(o->expression), "`%s` truncated to `%s`", a, b); } else { - checker_err(c, ast_node_token(o->expression), "`%s` overflows to `%s`", a, b); + error(&c->error_collector, ast_node_token(o->expression), "`%s` overflows to `%s`", a, b); } } else { - checker_err(c, ast_node_token(o->expression), "Cannot convert `%s` to `%s`", a, b); + error(&c->error_collector, ast_node_token(o->expression), "Cannot convert `%s` to `%s`", a, b); } o->mode = Addressing_Invalid; @@ -577,7 +575,7 @@ void check_unary_expression(Checker *c, Operand *o, Token op, AstNode *node) { if (o->mode != Addressing_Variable) { gbString str = expression_to_string(node->unary_expression.operand); defer (gb_string_free(str)); - checker_err(c, op, "Cannot take the pointer address of `%s`", str); + error(&c->error_collector, op, "Cannot take the pointer address of `%s`", str); o->mode = Addressing_Invalid; return; } @@ -646,7 +644,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) { } if (err_str) { - checker_err(c, op, "Cannot compare expression, %s", err_str); + error(&c->error_collector, op, "Cannot compare expression, %s", err_str); return; } @@ -697,7 +695,7 @@ void check_binary_expression(Checker *c, Operand *x, AstNode *node) { defer (gb_string_free(xt)); defer (gb_string_free(yt)); err_str = expression_to_string(x->expression); - checker_err(c, op, "Mismatched types in binary expression `%s` : `%s` vs `%s`", err_str, xt, yt); + error(&c->error_collector, op, "Mismatched types in binary expression `%s` : `%s` vs `%s`", err_str, xt, yt); } x->mode = Addressing_Invalid; return; @@ -728,7 +726,7 @@ void check_binary_expression(Checker *c, Operand *x, AstNode *node) { } if (fail) { - checker_err(c, ast_node_token(y->expression), "Division by zero not allowed"); + error(&c->error_collector, ast_node_token(y->expression), "Division by zero not allowed"); x->mode = Addressing_Invalid; return; } @@ -805,7 +803,7 @@ void convert_untyped_error(Checker *c, Operand *operand, Type *target_type) { extra_text = " - Did you want `null`?"; } } - checker_err(c, ast_node_token(operand->expression), "Cannot convert `%s` to `%s`%s", expr_str, type_str, extra_text); + error(&c->error_collector, ast_node_token(operand->expression), "Cannot convert `%s` to `%s`%s", expr_str, type_str, extra_text); operand->mode = Addressing_Invalid; } @@ -896,7 +894,7 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *valu if (!is_type_integer(operand.type)) { gbString expr_str = expression_to_string(operand.expression); - checker_err(c, ast_node_token(operand.expression), + error(&c->error_collector, ast_node_token(operand.expression), "Index `%s` must be an integer", expr_str); gb_string_free(expr_str); if (value) *value = 0; @@ -908,7 +906,7 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *valu i64 i = exact_value_to_integer(operand.value).value_integer; if (i < 0) { gbString expr_str = expression_to_string(operand.expression); - checker_err(c, ast_node_token(operand.expression), + error(&c->error_collector, ast_node_token(operand.expression), "Index `%s` cannot be a negative value", expr_str); gb_string_free(expr_str); if (value) *value = 0; @@ -919,7 +917,7 @@ b32 check_index_value(Checker *c, AstNode *index_value, i64 max_count, i64 *valu if (i >= max_count) { gbString expr_str = expression_to_string(operand.expression); - checker_err(c, ast_node_token(operand.expression), + error(&c->error_collector, ast_node_token(operand.expression), "Index `%s` is out of bounds range [0, %lld)", expr_str, max_count); gb_string_free(expr_str); return false; @@ -973,7 +971,7 @@ void check_selector(Checker *c, Operand *operand, AstNode *node) { gbString sel_str = expression_to_string(selector); defer (gb_string_free(op_str)); defer (gb_string_free(sel_str)); - checker_err(c, ast_node_token(op_expr), "`%s` has no field `%s`", op_str, sel_str); + error(&c->error_collector, ast_node_token(op_expr), "`%s` has no field `%s`", op_str, sel_str); operand->mode = Addressing_Invalid; operand->expression = node; return; @@ -1002,9 +1000,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) if (ce->arg_list_count > bp->arg_count && !bp->variadic) err = "Too many"; if (err) { - checker_err(c, ce->close, "`%s` arguments for `%.*s`, expected %td, got %td", - err, LIT(call->call_expression.proc->identifier.token.string), - bp->arg_count, ce->arg_list_count); + error(&c->error_collector, ce->close, "`%s` arguments for `%.*s`, expected %td, got %td", + err, LIT(call->call_expression.proc->identifier.token.string), + bp->arg_count, ce->arg_list_count); return false; } } @@ -1024,7 +1022,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) // size_of :: proc(Type) Type *type = check_type(c, ce->arg_list); if (!type) { - checker_err(c, ast_node_token(ce->arg_list), "Expected a type for `size_of`"); + error(&c->error_collector, ast_node_token(ce->arg_list), "Expected a type for `size_of`"); return false; } @@ -1049,7 +1047,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) // align_of :: proc(Type) Type *type = check_type(c, ce->arg_list); if (!type) { - checker_err(c, ast_node_token(ce->arg_list), "Expected a type for `align_of`"); + error(&c->error_collector, ast_node_token(ce->arg_list), "Expected a type for `align_of`"); return false; } operand->mode = Addressing_Constant; @@ -1074,12 +1072,12 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) AstNode *field_arg = unparen_expression(ce->arg_list->next); if (type) { if (type->kind != Type_Structure) { - checker_err(c, ast_node_token(ce->arg_list), "Expected a structure type for `offset_of`"); + error(&c->error_collector, ast_node_token(ce->arg_list), "Expected a structure type for `offset_of`"); return false; } if (field_arg == NULL || field_arg->kind != AstNode_Identifier) { - checker_err(c, ast_node_token(field_arg), "Expected an identifier for field argument"); + error(&c->error_collector, ast_node_token(field_arg), "Expected an identifier for field argument"); return false; } } @@ -1088,8 +1086,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) Entity *entity = lookup_field(type, field_arg, &index); if (entity == NULL) { gbString type_str = type_to_string(type); - checker_err(c, ast_node_token(ce->arg_list), - "`%s` has no field named `%.*s`", type_str, LIT(field_arg->identifier.token.string)); + error(&c->error_collector, ast_node_token(ce->arg_list), + "`%s` has no field named `%.*s`", type_str, LIT(field_arg->identifier.token.string)); return false; } @@ -1103,7 +1101,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) AstNode *arg = unparen_expression(ce->arg_list); if (arg->kind != AstNode_SelectorExpression) { gbString str = expression_to_string(arg); - checker_err(c, ast_node_token(arg), "`%s` is not a selector expression", str); + error(&c->error_collector, ast_node_token(arg), "`%s` is not a selector expression", str); return false; } auto *s = &arg->selector_expression; @@ -1123,8 +1121,8 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) Entity *entity = lookup_field(type, s->selector, &index); if (entity == NULL) { gbString type_str = type_to_string(type); - checker_err(c, ast_node_token(arg), - "`%s` has no field named `%.*s`", type_str, LIT(s->selector->identifier.token.string)); + error(&c->error_collector, ast_node_token(arg), + "`%s` has no field named `%.*s`", type_str, LIT(s->selector->identifier.token.string)); return false; } @@ -1141,15 +1139,15 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) !is_type_boolean(operand->type)) { gbString str = expression_to_string(ce->arg_list); defer (gb_string_free(str)); - checker_err(c, ast_node_token(call), - "`%s` is not a constant boolean", str); + error(&c->error_collector, ast_node_token(call), + "`%s` is not a constant boolean", str); return false; } if (!operand->value.value_bool) { gbString str = expression_to_string(ce->arg_list); defer (gb_string_free(str)); - checker_err(c, ast_node_token(call), - "Static assertion: `%s`", str); + error(&c->error_collector, ast_node_token(call), + "Static assertion: `%s`", str); return true; } break; @@ -1188,9 +1186,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) if (mode == Addressing_Invalid) { gbString str = expression_to_string(operand->expression); - checker_err(c, ast_node_token(operand->expression), - "Invalid expression `%s` for `%.*s`", - str, LIT(bp->name)); + error(&c->error_collector, ast_node_token(operand->expression), + "Invalid expression `%s` for `%.*s`", + str, LIT(bp->name)); gb_string_free(str); return false; } @@ -1219,7 +1217,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) src_type = s->slice.element; if (dest_type == NULL || src_type == NULL) { - checker_err(c, ast_node_token(call), "`copy` only expects slices as arguments"); + error(&c->error_collector, ast_node_token(call), "`copy` only expects slices as arguments"); return false; } @@ -1232,9 +1230,9 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) defer (gb_string_free(s_arg)); defer (gb_string_free(d_str)); defer (gb_string_free(s_str)); - checker_err(c, ast_node_token(call), - "Arguments to `copy`, %s, %s, have different element types: %s vs %s", - d_arg, s_arg, d_str, s_str); + error(&c->error_collector, ast_node_token(call), + "Arguments to `copy`, %s, %s, have different element types: %s vs %s", + d_arg, s_arg, d_str, s_str); return false; } @@ -1259,7 +1257,7 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) src_type = s; if (dest_type == NULL || src_type == NULL) { - checker_err(c, ast_node_token(call), "`copy_bytes` only expects pointers for the destintation and source"); + error(&c->error_collector, ast_node_token(call), "`copy_bytes` only expects pointers for the destintation and source"); return false; } @@ -1273,13 +1271,13 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) op.type->basic.kind != Basic_int) { gbString str = type_to_string(op.type); defer (gb_string_free(str)); - checker_err(c, ast_node_token(call), "`copy_bytes` 3rd argument must be of type `int`, a `%s` was given", str); + error(&c->error_collector, ast_node_token(call), "`copy_bytes` 3rd argument must be of type `int`, a `%s` was given", str); return false; } if (op.mode == Addressing_Constant) { if (exact_value_to_integer(op.value).value_integer <= 0) { - checker_err(c, ast_node_token(call), "You cannot copy a zero or negative amount of bytes with `copy_bytes`"); + error(&c->error_collector, ast_node_token(call), "You cannot copy a zero or negative amount of bytes with `copy_bytes`"); return false; } } @@ -1372,7 +1370,7 @@ void check_call_arguments(Checker *c, Operand *operand, Type *proc_type, AstNode } gbString proc_str = expression_to_string(ce->proc); - checker_err(c, ast_node_token(call), err_fmt, proc_str, param_count); + error(&c->error_collector, ast_node_token(call), err_fmt, proc_str, param_count); gb_string_free(proc_str); operand->mode = Addressing_Invalid; @@ -1406,7 +1404,7 @@ ExpressionKind check_call_expression(Checker *c, Operand *operand, AstNode *call AstNode *e = operand->expression; gbString str = expression_to_string(e); defer (gb_string_free(str)); - checker_err(c, ast_node_token(e), "Cannot call a non-procedure: `%s`", str); + error(&c->error_collector, ast_node_token(e), "Cannot call a non-procedure: `%s`", str); operand->mode = Addressing_Invalid; operand->expression = call; @@ -1494,7 +1492,7 @@ void check_cast_expression(Checker *c, Operand *operand, Type *type) { gbString type_str = type_to_string(type); defer (gb_string_free(expr_str)); defer (gb_string_free(type_str)); - checker_err(c, ast_node_token(operand->expression), "Cannot cast `%s` to `%s`", expr_str, type_str); + error(&c->error_collector, ast_node_token(operand->expression), "Cannot cast `%s` to `%s`", expr_str, type_str); operand->mode = Addressing_Invalid; return; @@ -1503,6 +1501,28 @@ void check_cast_expression(Checker *c, Operand *operand, Type *type) { operand->type = type; } +void check_expression_with_type_hint(Checker *c, Operand *o, AstNode *e, Type *t) { + check_expression_base(c, o, e, t); + check_not_tuple(c, o); + char *err_str = NULL; + switch (o->mode) { + case Addressing_NoValue: + err_str = "used as a value"; + break; + case Addressing_Type: + err_str = "is not an expression"; + break; + case Addressing_Builtin: + err_str = "must be called"; + break; + } + if (err_str != NULL) { + gbString str = expression_to_string(e); + defer (gb_string_free(str)); + error(&c->error_collector, ast_node_token(e), "`%s` %s", str, err_str); + o->mode = Addressing_Invalid; + } +} ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Type *type_hint) { @@ -1534,20 +1554,104 @@ ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Typ } break; case AstNode_ProcedureLiteral: { - Scope *origin_curr_scope = c->proc_context.scope; + Scope *origin_curr_scope = c->context.scope; Type *proc_type = check_type(c, node->procedure_literal.type); if (proc_type != NULL) { - check_procedure_body(c, empty_token, c->proc_context.decl, proc_type, node->procedure_literal.body); + check_procedure_body(c, empty_token, c->context.decl, proc_type, node->procedure_literal.body); o->mode = Addressing_Value; o->type = proc_type; } else { gbString str = expression_to_string(node); - checker_err(c, ast_node_token(node), "Invalid procedure literal `%s`", str); + error(&c->error_collector, ast_node_token(node), "Invalid procedure literal `%s`", str); gb_string_free(str); goto error; } } break; + case AstNode_CompoundLiteral: { + auto *cl = &node->compound_literal; + Type *type = type_hint; + if (cl->type_expression != NULL) { + type = check_type(c, cl->type_expression); + } + + if (type == NULL) { + error(&c->error_collector, ast_node_token(node), "Missing type in compound literal"); + goto error; + } + + Type *t = get_base_type(type); + switch (t->kind) { + case Type_Structure: { + if (cl->element_count == 0) + break; // NOTE(bill): No need to init + { // Checker values + AstNode *elem = cl->element_list; + isize field_count = t->structure.field_count; + isize index = 0; + for (; + elem != NULL; + elem = elem->next, index++) { + Entity *field = t->structure.fields[index]; + + check_expression(c, o, elem); + if (index >= field_count) { + error(&c->error_collector, ast_node_token(o->expression), "Too many values in structure literal, expected %td", field_count); + break; + } + check_assignment(c, o, field->type, make_string("structure literal")); + } + if (cl->element_count < field_count) { + error(&c->error_collector, node->compound_literal.close, "Too few values in structure literal, expected %td, got %td", field_count, cl->element_count); + } + } + + } break; + + case Type_Slice: + case Type_Array: + { + Type *element_type = NULL; + String context_name = {}; + if (t->kind == Type_Slice) { + element_type = t->slice.element; + context_name = make_string("slice literal"); + } else { + element_type = t->array.element; + context_name = make_string("array literal"); + } + + + i64 index = 0; + i64 max = 0; + for (AstNode *elem = cl->element_list; elem != NULL; elem = elem->next, index++) { + AstNode *e = elem; + if (t->kind == Type_Array && + t->array.count >= 0 && + index >= t->array.count) { + error(&c->error_collector, ast_node_token(elem), "Index %lld is out of bounds (>= %lld)", index, t->array.count); + } + + Operand o = {}; + check_expression_with_type_hint(c, &o, e, element_type); + check_assignment(c, &o, element_type, context_name); + } + if (max < index) + max = index; + } break; + + default: { + gbString str = type_to_string(t); + error(&c->error_collector, ast_node_token(node), "Invalid compound literal type `%s`", str); + gb_string_free(str); + goto error; + } break; + } + + o->mode = Addressing_Value; + o->type = type; + } break; + case AstNode_ParenExpression: kind = check_expression_base(c, o, node->paren_expression.expression, type_hint); o->expression = node; @@ -1555,7 +1659,7 @@ ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Typ case AstNode_TagExpression: // TODO(bill): Tag expressions - checker_err(c, ast_node_token(node), "Tag expressions are not supported yet"); + error(&c->error_collector, ast_node_token(node), "Tag expressions are not supported yet"); kind = check_expression_base(c, o, node->tag_expression.expression, type_hint); o->expression = node; break; @@ -1624,14 +1728,14 @@ ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Typ if (!valid) { gbString str = expression_to_string(o->expression); - checker_err(c, ast_node_token(o->expression), "Cannot index `%s`", str); + error(&c->error_collector, ast_node_token(o->expression), "Cannot index `%s`", str); gb_string_free(str); goto error; } if (node->index_expression.value == NULL) { gbString str = expression_to_string(o->expression); - checker_err(c, ast_node_token(o->expression), "Missing index for `%s`", str); + error(&c->error_collector, ast_node_token(o->expression), "Missing index for `%s`", str); gb_string_free(str); goto error; } @@ -1665,7 +1769,7 @@ ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Typ max_count = t->array.count; if (o->mode != Addressing_Variable) { gbString str = expression_to_string(node); - checker_err(c, ast_node_token(node), "Cannot slice array `%s`, value is not addressable", str); + error(&c->error_collector, ast_node_token(node), "Cannot slice array `%s`, value is not addressable", str); gb_string_free(str); goto error; } @@ -1687,7 +1791,7 @@ ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Typ if (!valid) { gbString str = expression_to_string(o->expression); - checker_err(c, ast_node_token(o->expression), "Cannot slice `%s`", str); + error(&c->error_collector, ast_node_token(o->expression), "Cannot slice `%s`", str); gb_string_free(str); goto error; } @@ -1715,7 +1819,7 @@ ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Typ for (isize j = i+1; j < gb_count_of(indices); j++) { i64 b = indices[j]; if (a > b && b >= 0) { - checker_err(c, se->close, "Invalid slice indices: [%td > %td]", a, b); + error(&c->error_collector, se->close, "Invalid slice indices: [%td > %td]", a, b); } } } @@ -1744,7 +1848,7 @@ ExpressionKind check__expression_base(Checker *c, Operand *o, AstNode *node, Typ o->type = t->pointer.element; } else { gbString str = expression_to_string(o->expression); - checker_err(c, ast_node_token(o->expression), "Cannot dereference `%s`", str); + error(&c->error_collector, ast_node_token(o->expression), "Cannot dereference `%s`", str); gb_string_free(str); goto error; } @@ -1812,11 +1916,11 @@ void check_multi_expression(Checker *c, Operand *o, AstNode *e) { case Addressing_NoValue: err_str = expression_to_string(e); - checker_err(c, ast_node_token(e), "`%s` used as value", err_str); + error(&c->error_collector, ast_node_token(e), "`%s` used as value", err_str); break; case Addressing_Type: err_str = expression_to_string(e); - checker_err(c, ast_node_token(e), "`%s` is not an expression", err_str); + error(&c->error_collector, ast_node_token(e), "`%s` is not an expression", err_str); break; } o->mode = Addressing_Invalid; @@ -1829,8 +1933,8 @@ void check_not_tuple(Checker *c, Operand *o) { if (o->type->kind == Type_Tuple) { isize count = o->type->tuple.variable_count; GB_ASSERT(count != 1); - checker_err(c, ast_node_token(o->expression), - "%td-valued tuple found where single value expected", count); + error(&c->error_collector, ast_node_token(o->expression), + "%td-valued tuple found where single value expected", count); o->mode = Addressing_Invalid; } } @@ -1849,8 +1953,8 @@ void check_expression_or_type(Checker *c, Operand *o, AstNode *e) { AstNode *e = o->expression; gbString str = expression_to_string(e); defer (gb_string_free(str)); - checker_err(c, ast_node_token(e), - "`%s` used as value or type", str); + error(&c->error_collector, ast_node_token(e), + "`%s` used as value or type", str); o->mode = Addressing_Invalid; } } @@ -1898,14 +2002,17 @@ gbString write_expression_to_string(gbString str, AstNode *node) { case AstNode_Identifier: str = string_append_token(str, node->identifier.token); break; - case AstNode_BasicLiteral: str = string_append_token(str, node->basic_literal); break; - case AstNode_ProcedureLiteral: str = write_expression_to_string(str, node->procedure_literal.type); break; + case AstNode_CompoundLiteral: + str = gb_string_appendc(str, "("); + str = write_expression_to_string(str, node->compound_literal.type_expression); + str = gb_string_appendc(str, " literal)"); + break; case AstNode_TagExpression: str = gb_string_appendc(str, "#"); diff --git a/src/checker/statements.cpp b/src/checker/statements.cpp index ba2c8e8b4..55dba44fc 100644 --- a/src/checker/statements.cpp +++ b/src/checker/statements.cpp @@ -9,29 +9,23 @@ void check_statement_list(Checker *c, AstNode *node) { // NOTE(bill): The last expression has to be a `return` statement -// TODO(bill): This is a mild hack and should be probably handled +// TODO(bill): This is a mild hack and should be probably handled properly // TODO(bill): Warn/err against code after `return` that it won't be executed -b32 check_is_terminating(Checker *c, AstNode *node); - -b32 check_is_terminating_list(Checker *c, AstNode *node_list) { - AstNode *end_of_list = node_list; - for (; end_of_list != NULL; end_of_list = end_of_list->next) { - if (end_of_list->next == NULL) - break; - } - - for (AstNode *node = end_of_list; node != NULL; node = node->prev) { - if (node->kind == AstNode_EmptyStatement) - continue; - return check_is_terminating(c, node); - } - return false; -} - b32 check_is_terminating(Checker *c, AstNode *node) { switch (node->kind) { - case AstNode_BlockStatement: - return check_is_terminating_list(c, node->block_statement.list); + case AstNode_BlockStatement: { + for (; node != NULL; node = node->next) { + if (node->next == NULL) + break; + } + + for (AstNode *n = node; node != NULL; n = n->prev) { + if (n->kind == AstNode_EmptyStatement) + continue; + return check_is_terminating(c, n); + } + return false; + } case AstNode_ExpressionStatement: return check_is_terminating(c, node->expression_statement.expression); @@ -138,12 +132,12 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n // TODO(bill): is this a good enough error message? - checker_err(c, ast_node_token(operand->expression), - "Cannot assign value `%s` of type `%s` to `%s` in %.*s", - expr_str, - op_type_string, - type_string, - LIT(context_name)); + error(&c->error_collector, ast_node_token(operand->expression), + "Cannot assign value `%s` of type `%s` to `%s` in %.*s", + expr_str, + op_type_string, + type_string, + LIT(context_name)); operand->mode = Addressing_Invalid; } @@ -151,7 +145,7 @@ void check_assignment(Checker *c, Operand *operand, Type *type, String context_n } -Type *check_assign_variable(Checker *c, Operand *op_a, AstNode *lhs) { +Type *check_assignment_variable(Checker *c, Operand *op_a, AstNode *lhs) { if (op_a->mode == Addressing_Invalid || op_a->type == &basic_types[Basic_Invalid]) { return NULL; @@ -172,8 +166,7 @@ Type *check_assign_variable(Checker *c, Operand *op_a, AstNode *lhs) { Entity *e = NULL; b32 used = false; if (node->kind == AstNode_Identifier) { - scope_lookup_parent_entity(c->proc_context.scope, node->identifier.token.string, - NULL, &e); + e = scope_lookup_entity(c->context.scope, node->identifier.token.string); if (e != NULL && e->kind == Entity_Variable) { used = e->variable.used; // TODO(bill): Make backup just in case } @@ -203,7 +196,7 @@ Type *check_assign_variable(Checker *c, Operand *op_a, AstNode *lhs) { gbString str = expression_to_string(op_b.expression); defer (gb_string_free(str)); - checker_err(c, ast_node_token(op_b.expression), "Cannot assign to `%s`", str); + error(&c->error_collector, ast_node_token(op_b.expression), "Cannot assign to `%s`", str); } break; } @@ -214,33 +207,6 @@ Type *check_assign_variable(Checker *c, Operand *op_a, AstNode *lhs) { return op_a->type; } -// TODO(bill): Do I need to pass the *_count? -void check_assign_variables(Checker *c, - AstNode *lhs_list, isize lhs_count, - AstNode *rhs_list, isize rhs_count) { - Operand operand = {Addressing_Invalid}; - AstNode *lhs = lhs_list, *rhs = rhs_list; - for (; - lhs != NULL && rhs != NULL; - lhs = lhs->next, rhs = rhs->next) { - check_multi_expression(c, &operand, rhs); - if (operand.type->kind != Type_Tuple) { - check_assign_variable(c, &operand, lhs); - } else { - auto *tuple = &operand.type->tuple; - for (isize i = 0; - i < tuple->variable_count && lhs != NULL; - i++, lhs = lhs->next) { - // TODO(bill): More error checking - operand.type = tuple->variables[i]->type; - check_assign_variable(c, &operand, lhs); - } - if (lhs == NULL) - break; - } - } -} - // NOTE(bill): `content_name` is for debugging Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String context_name) { if (operand->mode == Addressing_Invalid || @@ -256,7 +222,7 @@ Type *check_init_variable(Checker *c, Entity *e, Operand *operand, String contex Type *t = operand->type; if (is_type_untyped(t)) { if (t == &basic_types[Basic_Invalid]) { - checker_err(c, e->token, "Use of untyped thing in %.*s", LIT(context_name)); + error(&c->error_collector, e->token, "Use of untyped thing in %.*s", LIT(context_name)); e->type = &basic_types[Basic_Invalid]; return NULL; } @@ -299,9 +265,9 @@ void check_init_variables(Checker *c, Entity **lhs, isize lhs_count, AstNode *in if (i < lhs_count && i < init_count) { if (lhs[i]->type == NULL) - checker_err(c, lhs[i]->token, "Too few values on the right hand side of the declaration"); + error(&c->error_collector, lhs[i]->token, "Too few values on the right hand side of the declaration"); } else if (rhs != NULL) { - checker_err(c, ast_node_token(rhs), "Too many values on the right hand side of the declaration"); + error(&c->error_collector, ast_node_token(rhs), "Too many values on the right hand side of the declaration"); } } @@ -316,8 +282,8 @@ void check_init_constant(Checker *c, Entity *e, Operand *operand) { if (operand->mode != Addressing_Constant) { // TODO(bill): better error - checker_err(c, ast_node_token(operand->expression), - "`%.*s` is not a constant", LIT(ast_node_token(operand->expression).string)); + error(&c->error_collector, ast_node_token(operand->expression), + "`%.*s` is not a constant", LIT(ast_node_token(operand->expression).string)); if (e->type == NULL) e->type = &basic_types[Basic_Invalid]; return; @@ -352,8 +318,8 @@ void check_constant_declaration(Checker *c, Entity *e, AstNode *type_expr, AstNo if (!is_type_constant_type(t)) { gbString str = type_to_string(t); defer (gb_string_free(str)); - checker_err(c, ast_node_token(type_expr), - "Invalid constant type `%s`", str); + error(&c->error_collector, ast_node_token(type_expr), + "Invalid constant type `%s`", str); e->type = &basic_types[Basic_Invalid]; return; } @@ -382,20 +348,20 @@ void check_type_declaration(Checker *c, Entity *e, AstNode *type_expr, Type *nam void check_procedure_body(Checker *c, Token token, DeclarationInfo *decl, Type *type, AstNode *body) { GB_ASSERT(body->kind == AstNode_BlockStatement); - ProcedureContext old_proc_context = c->proc_context; - c->proc_context.scope = decl->scope; - c->proc_context.decl = decl; + CheckerContext old_context = c->context; + c->context.scope = decl->scope; + c->context.decl = decl; push_procedure(c, type); check_statement_list(c, body->block_statement.list); if (type->procedure.results_count > 0) { if (!check_is_terminating(c, body)) { - checker_err(c, body->block_statement.close, "Missing return statement at the end of the procedure"); + error(&c->error_collector, body->block_statement.close, "Missing return statement at the end of the procedure"); } } pop_procedure(c); - c->proc_context = old_proc_context; + c->context = old_context; } void check_procedure_declaration(Checker *c, Entity *e, DeclarationInfo *d, b32 check_body_later) { @@ -406,8 +372,8 @@ void check_procedure_declaration(Checker *c, Entity *e, DeclarationInfo *d, b32 auto *pd = &d->proc_decl->procedure_declaration; #if 1 - Scope *original_curr_scope = c->proc_context.scope; - c->proc_context.scope = c->global_scope; + Scope *original_curr_scope = c->context.scope; + c->context.scope = c->global_scope; check_open_scope(c, pd->procedure_type); #endif check_procedure_type(c, proc_type, pd->procedure_type); @@ -425,23 +391,23 @@ void check_procedure_declaration(Checker *c, Entity *e, DeclarationInfo *d, b32 } else if (are_strings_equal(tag_name, make_string("no_inline"))) { is_no_inline = true; } else { - checker_err(c, ast_node_token(tag), "Unknown procedure tag"); + error(&c->error_collector, ast_node_token(tag), "Unknown procedure tag"); } // TODO(bill): Other tags } if (is_inline && is_no_inline) { - checker_err(c, ast_node_token(pd->tag_list), - "You cannot apply both `inline` and `no_inline` to a procedure"); + error(&c->error_collector, ast_node_token(pd->tag_list), + "You cannot apply both `inline` and `no_inline` to a procedure"); } if (pd->body != NULL) { if (is_foreign) { - checker_err(c, ast_node_token(pd->body), - "A procedure tagged as `#foreign` cannot have a body"); + error(&c->error_collector, ast_node_token(pd->body), + "A procedure tagged as `#foreign` cannot have a body"); } - d->scope = c->proc_context.scope; + d->scope = c->context.scope; GB_ASSERT(pd->body->kind == AstNode_BlockStatement); if (check_body_later) { @@ -453,7 +419,7 @@ void check_procedure_declaration(Checker *c, Entity *e, DeclarationInfo *d, b32 #if 1 check_close_scope(c); - c->proc_context.scope = original_curr_scope; + c->context.scope = original_curr_scope; #endif } @@ -506,11 +472,11 @@ void check_entity_declaration(Checker *c, Entity *e, Type *named_type) { switch (e->kind) { case Entity_Constant: - c->proc_context.decl = d; + c->context.decl = d; check_constant_declaration(c, e, d->type_expr, d->init_expr); break; case Entity_Variable: - c->proc_context.decl = d; + c->context.decl = d; check_variable_declaration(c, e, d->entities, d->entity_count, d->type_expr, d->init_expr); break; case Entity_TypeName: @@ -537,19 +503,19 @@ void check_statement(Checker *c, AstNode *node) { ExpressionKind kind = check_expression_base(c, &operand, node->expression_statement.expression); switch (operand.mode) { case Addressing_Type: - checker_err(c, ast_node_token(node), "Is not an expression"); + error(&c->error_collector, ast_node_token(node), "Is not an expression"); break; default: if (kind == Expression_Statement) return; - checker_err(c, ast_node_token(node), "Expression is not used"); + error(&c->error_collector, ast_node_token(node), "Expression is not used"); break; } } break; case AstNode_TagStatement: // TODO(bill): Tag Statements - checker_err(c, ast_node_token(node), "Tag statements are not supported yet"); + error(&c->error_collector, ast_node_token(node), "Tag statements are not supported yet"); check_statement(c, node->tag_statement.statement); break; @@ -567,7 +533,7 @@ void check_statement(Checker *c, AstNode *node) { op.string.len = 1; break; default: - checker_err(c, s->op, "Unknown inc/dec operation %.*s", LIT(s->op.string)); + error(&c->error_collector, s->op, "Unknown inc/dec operation %.*s", LIT(s->op.string)); return; } @@ -576,7 +542,7 @@ void check_statement(Checker *c, AstNode *node) { if (operand.mode == Addressing_Invalid) return; if (!is_type_numeric(operand.type)) { - checker_err(c, s->op, "Non numeric type"); + error(&c->error_collector, s->op, "Non numeric type"); return; } @@ -594,25 +560,56 @@ void check_statement(Checker *c, AstNode *node) { case AstNode_AssignStatement: switch (node->assign_statement.op.kind) { - case Token_Eq: + case Token_Eq: { + // a, b, c = 1, 2, 3; // Multisided if (node->assign_statement.lhs_count == 0) { - checker_err(c, node->assign_statement.op, "Missing lhs in assignment statement"); + error(&c->error_collector, node->assign_statement.op, "Missing lhs in assignment statement"); return; } - check_assign_variables(c, - node->assign_statement.lhs_list, node->assign_statement.lhs_count, - node->assign_statement.rhs_list, node->assign_statement.rhs_count); - break; + + Operand operand = {}; + AstNode *lhs = node->assign_statement.lhs_list; + AstNode *rhs = node->assign_statement.rhs_list; + isize i = 0; + for (; + lhs != NULL && rhs != NULL; + lhs = lhs->next, rhs = rhs->next) { + check_multi_expression(c, &operand, rhs); + if (operand.type->kind != Type_Tuple) { + check_assignment_variable(c, &operand, lhs); + i++; + } else { + auto *tuple = &operand.type->tuple; + for (isize j = 0; + j < tuple->variable_count && lhs != NULL; + j++, i++, lhs = lhs->next) { + // TODO(bill): More error checking + operand.type = tuple->variables[j]->type; + check_assignment_variable(c, &operand, lhs); + } + if (lhs == NULL) + break; + } + } + + if (i < node->assign_statement.lhs_count && i < node->assign_statement.rhs_count) { + if (lhs == NULL) + error(&c->error_collector, ast_node_token(lhs), "Too few values on the right hand side of the declaration"); + } else if (rhs != NULL) { + error(&c->error_collector, ast_node_token(rhs), "Too many values on the right hand side of the declaration"); + } + } break; default: { + // a += 1; // Single-sided Token op = node->assign_statement.op; if (node->assign_statement.lhs_count != 1 || node->assign_statement.rhs_count != 1) { - checker_err(c, op, "Assignment operation `%.*s` requires single-valued expressions", LIT(op.string)); + error(&c->error_collector, op, "Assignment operation `%.*s` requires single-valued expressions", LIT(op.string)); return; } if (!gb_is_between(op.kind, Token_AddEq, Token_ModEq)) { - checker_err(c, op, "Unknown Assignment operation `%.*s`", LIT(op.string)); + error(&c->error_collector, op, "Unknown Assignment operation `%.*s`", LIT(op.string)); return; } // TODO(bill): Check if valid assignment operator @@ -627,7 +624,7 @@ void check_statement(Checker *c, AstNode *node) { if (operand.mode == Addressing_Invalid) return; // NOTE(bill): Only use the first one will be used - check_assign_variable(c, &operand, node->assign_statement.lhs_list); + check_assignment_variable(c, &operand, node->assign_statement.lhs_list); } break; } break; @@ -643,7 +640,7 @@ void check_statement(Checker *c, AstNode *node) { check_expression(c, &operand, node->if_statement.cond); if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) { - checker_err(c, ast_node_token(node->if_statement.cond), + error(&c->error_collector, ast_node_token(node->if_statement.cond), "Non-boolean condition in `if` statement"); } check_statement(c, node->if_statement.body); @@ -655,7 +652,7 @@ void check_statement(Checker *c, AstNode *node) { check_statement(c, node->if_statement.else_statement); break; default: - checker_err(c, ast_node_token(node->if_statement.else_statement), + error(&c->error_collector, ast_node_token(node->if_statement.else_statement), "Invalid `else` statement in `if` statement"); break; } @@ -667,7 +664,7 @@ void check_statement(Checker *c, AstNode *node) { GB_ASSERT(gb_array_count(c->procedure_stack) > 0); if (c->in_defer) { - checker_err(c, rs->token, "You cannot `return` within a defer statement"); + error(&c->error_collector, rs->token, "You cannot `return` within a defer statement"); // TODO(bill): Should I break here? break; } @@ -677,7 +674,7 @@ void check_statement(Checker *c, AstNode *node) { if (proc_type->procedure.results) result_count = proc_type->procedure.results->tuple.variable_count; if (result_count != rs->result_count) { - checker_err(c, rs->token, "Expected %td return %s, got %td", + error(&c->error_collector, rs->token, "Expected %td return %s, got %td", result_count, (result_count != 1 ? "values" : "value"), rs->result_count); @@ -698,8 +695,8 @@ void check_statement(Checker *c, AstNode *node) { check_expression(c, &operand, node->for_statement.cond); if (operand.mode != Addressing_Invalid && !is_type_boolean(operand.type)) { - checker_err(c, ast_node_token(node->for_statement.cond), - "Non-boolean condition in `for` statement"); + error(&c->error_collector, ast_node_token(node->for_statement.cond), + "Non-boolean condition in `for` statement"); } } check_statement(c, node->for_statement.end); @@ -709,7 +706,7 @@ void check_statement(Checker *c, AstNode *node) { case AstNode_DeferStatement: { auto *ds = &node->defer_statement; if (is_ast_node_declaration(ds->statement)) { - checker_err(c, ds->token, "You cannot defer a declaration"); + error(&c->error_collector, ds->token, "You cannot defer a declaration"); } else { b32 out_in_defer = c->in_defer; c->in_defer = true; @@ -739,10 +736,10 @@ void check_statement(Checker *c, AstNode *node) { // 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->proc_context.scope, str); + found = current_scope_lookup_entity(c->context.scope, str); } if (found == NULL) { - entity = make_entity_variable(c->allocator, c->proc_context.scope, token, NULL); + entity = make_entity_variable(c->allocator, c->context.scope, token, NULL); if (!can_be_ignored) { new_entities[new_entity_count++] = entity; } @@ -751,7 +748,7 @@ void check_statement(Checker *c, AstNode *node) { entity = found; } } else { - checker_err(c, token, "A variable declaration must be an identifier"); + 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); @@ -783,7 +780,7 @@ void check_statement(Checker *c, AstNode *node) { AstNode *name = vd->name_list; for (isize i = 0; i < new_entity_count; i++, name = name->next) { - add_entity(c, c->proc_context.scope, name, new_entities[i]); + add_entity(c, c->context.scope, name, new_entities[i]); } } break; @@ -794,7 +791,7 @@ void check_statement(Checker *c, AstNode *node) { name = name->next, value = value->next) { GB_ASSERT(name->kind == AstNode_Identifier); ExactValue v = {ExactValue_Invalid}; - Entity *e = make_entity_constant(c->allocator, c->proc_context.scope, name->identifier.token, NULL, v); + Entity *e = make_entity_constant(c->allocator, c->context.scope, name->identifier.token, NULL, v); entities[entity_index++] = e; check_constant_declaration(c, e, vd->type_expression, value); } @@ -804,27 +801,27 @@ void check_statement(Checker *c, AstNode *node) { // TODO(bill): Better error messages or is this good enough? if (rhs_count == 0 && vd->type_expression == NULL) { - checker_err(c, ast_node_token(node), "Missing type or initial expression"); + error(&c->error_collector, ast_node_token(node), "Missing type or initial expression"); } else if (lhs_count < rhs_count) { - checker_err(c, ast_node_token(node), "Extra initial expression"); + 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->proc_context.scope, name, entities[i]); + add_entity(c, c->context.scope, name, entities[i]); } } break; default: - checker_err(c, ast_node_token(node), "Unknown variable declaration kind. Probably an invalid AST."); + error(&c->error_collector, ast_node_token(node), "Unknown variable declaration kind. Probably an invalid AST."); return; } } break; case AstNode_ProcedureDeclaration: { auto *pd = &node->procedure_declaration; - Entity *e = make_entity_procedure(c->allocator, c->proc_context.scope, pd->name->identifier.token, NULL); - add_entity(c, c->proc_context.scope, pd->name, e); + Entity *e = make_entity_procedure(c->allocator, c->context.scope, pd->name->identifier.token, NULL); + add_entity(c, c->context.scope, pd->name, e); DeclarationInfo decl = {}; init_declaration_info(&decl, e->parent); @@ -836,8 +833,8 @@ void check_statement(Checker *c, AstNode *node) { case AstNode_TypeDeclaration: { auto *td = &node->type_declaration; AstNode *name = td->name; - Entity *e = make_entity_type_name(c->allocator, c->proc_context.scope, name->identifier.token, NULL); - add_entity(c, c->proc_context.scope, name, e); + Entity *e = make_entity_type_name(c->allocator, c->context.scope, name->identifier.token, NULL); + add_entity(c, c->context.scope, name, e); check_type_declaration(c, e, td->type_expression, NULL); } break; } diff --git a/src/main.cpp b/src/main.cpp index e4190714e..a41538f1b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -24,11 +24,14 @@ int main(int argc, char **argv) { parse_files(&parser, init_filename); + // print_ast(parser.files[0].declarations, 0); + Checker checker = {}; init_checker(&checker, &parser); defer (destroy_checker(&checker)); check_parsed_files(&checker); + #if 0 Codegen codegen = {}; if (init_codegen(&codegen, &checker)) { diff --git a/src/parser.cpp b/src/parser.cpp index becf1ae81..5ff69589c 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -18,8 +18,7 @@ struct AstFile { isize scope_level; isize error_count; - isize error_prev_line; - isize error_prev_column; + TokenPos error_prev_pos; }; @@ -49,6 +48,7 @@ enum AstNodeKind { AstNode_BasicLiteral, AstNode_Identifier, AstNode_ProcedureLiteral, + AstNode_CompoundLiteral, AstNode__ExpressionBegin, AstNode_BadExpression, // NOTE(bill): Naughty expression @@ -126,6 +126,12 @@ struct AstNode { AstNode *type; // AstNode_ProcedureType AstNode *body; // AstNode_BlockStatement } procedure_literal; + struct { + AstNode *type_expression; + AstNode *element_list; + isize element_count; + Token open, close; + } compound_literal; struct { Token token; @@ -281,6 +287,8 @@ Token ast_node_token(AstNode *node) { return node->identifier.token; case AstNode_ProcedureLiteral: return ast_node_token(node->procedure_literal.type); + case AstNode_CompoundLiteral: + return ast_node_token(node->compound_literal.type_expression); case AstNode_TagExpression: return node->tag_expression.token; case AstNode_BadExpression: @@ -409,18 +417,17 @@ AstEntity *ast_scope_insert(AstScope *scope, AstEntity entity) { #define ast_file_err(f, token, fmt, ...) ast_file_err_(f, __FUNCTION__, token, fmt, ##__VA_ARGS__) void ast_file_err_(AstFile *file, char *function, Token token, char *fmt, ...) { // NOTE(bill): Duplicate error, skip it - if (file->error_prev_line != token.line || file->error_prev_column != token.column) { + if (!token_pos_are_equal(file->error_prev_pos, token.pos)) { va_list va; - file->error_prev_line = token.line; - file->error_prev_column = token.column; + file->error_prev_pos = token.pos; #if 0 gb_printf_err("%s()\n", function); #endif va_start(va, fmt); gb_printf_err("%.*s(%td:%td) Syntax error: %s\n", - LIT(file->tokenizer.fullpath), token.line, token.column, + LIT(token.pos.file), token.pos.line, token.pos.column, gb_bprintf_va(fmt, va)); va_end(va); } @@ -573,6 +580,18 @@ gb_inline AstNode *make_procedure_literal(AstFile *f, AstNode *type, AstNode *bo return result; } + +gb_inline AstNode *make_compound_literal(AstFile *f, AstNode *type_expression, AstNode *element_list, isize element_count, + Token open, Token close) { + AstNode *result = make_node(f, AstNode_CompoundLiteral); + result->compound_literal.type_expression = type_expression; + result->compound_literal.element_list = element_list; + result->compound_literal.element_count = element_count; + result->compound_literal.open = open; + result->compound_literal.close = close; + return result; +} + gb_inline AstNode *make_bad_statement(AstFile *f, Token begin, Token end) { AstNode *result = make_node(f, AstNode_BadStatement); result->bad_statement.begin = begin; @@ -802,9 +821,12 @@ gb_internal void add_ast_entity(AstFile *f, AstScope *scope, AstNode *declaratio if (insert_entity != NULL && !are_strings_equal(insert_entity->token.string, make_string("_"))) { ast_file_err(f, entity->token, - "There is already a previous declaration of `%.*s` in the current scope at (%td:%td)", + "There is already a previous declaration of `%.*s` in the current scope at\n" + "\t%.*s(%td:%td)", LIT(insert_entity->token.string), - insert_entity->token.line, insert_entity->token.column); + LIT(insert_entity->token.pos.file), + insert_entity->token.pos.line, + insert_entity->token.pos.column); } } } @@ -850,9 +872,55 @@ AstNode *unparen_expression(AstNode *node) { } } +AstNode *parse_value(AstFile *f); +AstNode *parse_element_list(AstFile *f, isize *element_count_) { + AstNode *root = NULL; + AstNode *curr = NULL; + isize element_count = 0; -AstNode *parse_atom_expression(AstFile *f, b32 lhs) { + while (f->cursor[0].kind != Token_CloseBrace && + f->cursor[0].kind != Token_EOF) { + AstNode *elem = parse_value(f); + #if 0 + // TODO(bill): Designated Initializers + if (f->cursor[0].kind == Token_Eq) { + Token eq = expect_token(f, Token_Eq); + } + #endif + DLIST_APPEND(root, curr, elem); + element_count++; + if (f->cursor[0].kind != Token_Comma) + break; + next_token(f); + } + + if (element_count_) *element_count_ = element_count; + return root; +} + +AstNode *parse_literal_value(AstFile *f, AstNode *type_expression) { + AstNode *element_list = NULL; + isize element_count = 0; + Token open = expect_token(f, Token_OpenBrace); + if (f->cursor[0].kind != Token_CloseBrace) + element_list = parse_element_list(f, &element_count); + Token close = expect_token(f, Token_CloseBrace); + + return make_compound_literal(f, type_expression, element_list, element_count, open, close); +} + +AstNode *parse_value(AstFile *f) { + if (f->cursor[0].kind == Token_OpenBrace) + return parse_literal_value(f, NULL); + + AstNode *value = parse_expression(f, false); + return value; +} + +AstNode *parse_identifier_or_type(AstFile *f); + +AstNode *parse_operand(AstFile *f, b32 lhs) { AstNode *operand = NULL; // Operand switch (f->cursor[0].kind) { case Token_Identifier: @@ -860,7 +928,7 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) { if (!lhs) { // TODO(bill): Handle? } - break; + return operand; case Token_Integer: case Token_Float: @@ -868,7 +936,7 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) { case Token_Rune: operand = make_basic_literal(f, f->cursor[0]); next_token(f); - break; + return operand; case Token_OpenParen: { Token open, close; @@ -877,11 +945,13 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) { operand = parse_expression(f, false); close = expect_token(f, Token_CloseParen); operand = make_paren_expression(f, operand, open, close); + return operand; } break; case Token_Hash: { operand = parse_tag_expression(f, NULL); operand->tag_expression.expression = parse_expression(f, false); + return operand; } break; // Parse Procedure Type or Literal @@ -895,8 +965,23 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) { AstNode *body = parse_body(f, scope); return make_procedure_literal(f, type, body); } break; + + default: { + AstNode *type = parse_identifier_or_type(f); + if (type != NULL) { + // NOTE(bill): Sanity check as identifiers should be handled already + GB_ASSERT_MSG(type->kind != AstNode_Identifier, "Type Cannot be identifier"); + return type; + } + } break; } + ast_file_err(f, f->cursor[0], "Expected an operand"); + return make_bad_expression(f, f->cursor[0], f->cursor[0]); +} + +AstNode *parse_atom_expression(AstFile *f, b32 lhs) { + AstNode *operand = parse_operand(f, lhs); b32 loop = true; while (loop) { @@ -998,6 +1083,24 @@ AstNode *parse_atom_expression(AstFile *f, b32 lhs) { operand = make_dereference_expression(f, operand, expect_token(f, Token_Pointer)); break; + case Token_OpenBrace: { + switch (operand->kind) { + case AstNode_BadExpression: + case AstNode_Identifier: + case AstNode_ArrayType: + case AstNode_StructType: { + if (lhs) { + // TODO(bill): Handle this + } + operand = parse_literal_value(f, operand); + } break; + + default: + loop = false; + break; + } + } break; + default: loop = false; break; @@ -1203,7 +1306,6 @@ AstNode *parse_identfier_list(AstFile *f, isize *list_count_) { } -AstNode *parse_identifier_or_type(AstFile *f); AstNode *parse_type_attempt(AstFile *f) { AstNode *type = parse_identifier_or_type(f); @@ -1315,13 +1417,22 @@ AstNode *parse_identifier_or_type(AstFile *f) { return make_paren_expression(f, type_expression, open, close); } +#if 0 + // TODO(bill): Why did I add this? case Token_Colon: case Token_Eq: break; +#endif + case Token_Colon: + break; + case Token_Eq: + if (f->cursor[-1].kind == Token_Colon) + break; + // fallthrough default: ast_file_err(f, f->cursor[0], - "Expected a type after `%.*s`, got `%.*s`", LIT(f->cursor[-1].string), LIT(f->cursor[0].string)); + "Expected a type after `%.*s`, got `%.*s`", LIT(f->cursor[-1].string), LIT(f->cursor[0].string)); break; } diff --git a/src/printer.cpp b/src/printer.cpp index f34dd4559..3ce94aefb 100644 --- a/src/printer.cpp +++ b/src/printer.cpp @@ -18,6 +18,20 @@ void print_ast(AstNode *node, isize indent) { print_indent(indent); print_token(node->identifier.token); break; + case AstNode_ProcedureLiteral: + print_indent(indent); + gb_printf("(proc lit)\n"); + print_ast(node->procedure_literal.type, indent+1); + print_ast(node->procedure_literal.body, indent+1); + break; + + case AstNode_CompoundLiteral: + print_indent(indent); + gb_printf("(compound lit)\n"); + print_ast(node->compound_literal.type_expression, indent+1); + print_ast(node->compound_literal.element_list, indent+1); + break; + case AstNode_TagExpression: print_indent(indent); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 4edf8ab1d..fd3683d91 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -209,18 +209,64 @@ char const *TOKEN_STRINGS[] = { }; -// NOTE(bill): Text is UTF-8, thus why u8 and not char -typedef struct Token Token; -struct Token { - TokenKind kind; - String string; +struct TokenPos { + String file; isize line, column; }; +b32 token_pos_are_equal(TokenPos a, TokenPos b) { + if (a.line == b.line) { + if (a.column == b.column) { + return are_strings_equal(a.file, b.file); + } + } + return false; + +} + +// NOTE(bill): Text is UTF-8, thus why u8 and not char +struct Token { + TokenKind kind; + String string; + TokenPos pos; +}; Token empty_token = {Token_Invalid}; +struct ErrorCollector { + TokenPos prev; + isize count; +}; + +void error(ErrorCollector *ec, Token token, char *fmt, ...) { + // NOTE(bill): Duplicate error, skip it + if (!token_pos_are_equal(ec->prev, token.pos)) { + ec->prev = token.pos; + + va_list va; + va_start(va, fmt); + gb_printf_err("%.*s(%td:%td) Error: %s\n", + LIT(token.pos.file), token.pos.line, token.pos.column, + gb_bprintf_va(fmt, va)); + va_end(va); + + } + ec->count++; +} + +void warning(Token token, char *fmt, ...) { + va_list va; + va_start(va, fmt); + gb_printf_err("%.*s(%td:%td) Warning: %s\n", + LIT(token.pos.file), token.pos.line, token.pos.column, + gb_bprintf_va(fmt, va)); + va_end(va); +} + + + + char const *token_kind_to_string(TokenKind kind) { return TOKEN_STRINGS[kind]; @@ -432,8 +478,9 @@ Token scan_number_to_token(Tokenizer *t, b32 seen_decimal_point) { u8 *start_curr = t->curr; token.kind = Token_Integer; token.string = make_string(start_curr, 1); - token.line = t->line_count; - token.column = t->curr-t->line+1; + token.pos.file = t->fullpath; + token.pos.line = t->line_count; + token.pos.column = t->curr-t->line+1; if (seen_decimal_point) { start_curr--; @@ -595,8 +642,9 @@ Token tokenizer_get_token(Tokenizer *t) { tokenizer_skip_whitespace(t); token.string = make_string(t->curr, 1); - token.line = t->line_count; - token.column = t->curr - t->line + 1; + token.pos.file = t->fullpath; + token.pos.line = t->line_count; + token.pos.column = t->curr - t->line + 1; curr_rune = t->curr_rune; if (rune_is_letter(curr_rune)) {