diff --git a/core/bufio/lookahead_reader.odin b/core/bufio/lookahead_reader.odin new file mode 100644 index 000000000..04ce2fb6b --- /dev/null +++ b/core/bufio/lookahead_reader.odin @@ -0,0 +1,83 @@ +package bufio + +import "core:io" + +// Loadahead_Reader provides io lookahead. +// This is useful for tokenizers/parsers. +// Loadahead_Reader is similar to bufio.Reader, but unlike bufio.Reader, Loadahead_Reader's buffer size +// will EXACTLY match the specified size, whereas bufio.Reader's buffer size may differ from the specified size. +// This makes sure that the buffer will not be accidentally read beyond the expected size. +Loadahead_Reader :: struct { + r: io.Reader, + buf: []byte, + n: int, +} + +lookahead_reader_init :: proc(lr: ^Loadahead_Reader, r: io.Reader, buf: []byte) -> ^Loadahead_Reader { + lr.r = r; + lr.buf = buf; + lr.n = 0; + return lr; +} + +lookahead_reader_buffer :: proc(lr: ^Loadahead_Reader) -> []byte { + return lr.buf[:lr.n]; +} + + +// lookahead_reader_peek returns a slice of the Lookahead_Reader which holds n bytes +// If the Lookahead_Reader cannot hold enough bytes, it will read from the underlying reader to populate the rest. +// NOTE: The returned buffer is not a copy of the underlying buffer +lookahead_reader_peek :: proc(lr: ^Loadahead_Reader, n: int) -> ([]byte, io.Error) { + switch { + case n < 0: + return nil, .Negative_Read; + case n > len(lr.buf): + return nil, .Buffer_Full; + } + + n := n; + err: io.Error; + read_count: int; + + if lr.n < n { + read_count, err = io.read_at_least(lr.r, lr.buf[lr.n:], n-lr.n); + if err == .Unexpected_EOF { + err = .EOF; + } + } + + lr.n += read_count; + + if n > lr.n { + n = lr.n; + } + return lr.buf[:n], err; +} + +// lookahead_reader_peek_all returns a slice of the Lookahead_Reader populating the full buffer +// If the Lookahead_Reader cannot hold enough bytes, it will read from the underlying reader to populate the rest. +// NOTE: The returned buffer is not a copy of the underlying buffer +lookahead_reader_peek_all :: proc(lr: ^Loadahead_Reader) -> ([]byte, io.Error) { + return lookahead_reader_peek(lr, len(lr.buf)); +} + + +// lookahead_reader_consume drops the first n populated bytes from the Lookahead_Reader. +lookahead_reader_consume :: proc(lr: ^Loadahead_Reader, n: int) -> io.Error { + switch { + case n == 0: + return nil; + case n < 0: + return .Negative_Read; + case lr.n < n: + return .Short_Buffer; + } + copy(lr.buf, lr.buf[n:lr.n]); + lr.n -= n; + return nil; +} + +lookahead_reader_consume_all :: proc(lr: ^Loadahead_Reader) -> io.Error { + return lookahead_reader_consume(lr, lr.n); +} diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index f08a33fde..378779dc6 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -445,38 +445,77 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 case BuiltinProc_offset_of: { + // offset_of :: proc(value.field) -> uintptr // offset_of :: proc(Type, field) -> uintptr - Operand op = {}; - Type *bt = check_type(c, ce->args[0]); - Type *type = base_type(bt); - if (type == nullptr || type == t_invalid) { - error(ce->args[0], "Expected a type for 'offset_of'"); + + Type *type = nullptr; + Ast *field_arg = nullptr; + + if (ce->args.count == 1) { + Ast *arg0 = unparen_expr(ce->args[0]); + if (arg0->kind != Ast_SelectorExpr) { + gbString x = expr_to_string(arg0); + error(ce->args[0], "Invalid expression for 'offset_of', '%s' is not a selector expression", x); + gb_string_free(x); + return false; + } + + ast_node(se, SelectorExpr, arg0); + + Operand x = {}; + check_expr(c, &x, se->expr); + if (x.mode == Addressing_Invalid) { + return false; + } + type = type_deref(x.type); + + Type *bt = base_type(type); + if (bt == nullptr || bt == t_invalid) { + error(ce->args[0], "Expected a type for 'offset_of'"); + return false; + } + + field_arg = unparen_expr(se->selector); + } else if (ce->args.count == 2) { + type = check_type(c, ce->args[0]); + Type *bt = base_type(type); + if (bt == nullptr || bt == t_invalid) { + error(ce->args[0], "Expected a type for 'offset_of'"); + return false; + } + + field_arg = unparen_expr(ce->args[1]); + } else { + error(ce->args[0], "Expected either 1 or 2 arguments to 'offset_of', in the format of 'offset_of(Type, field)', 'offset_of(value.field)'"); return false; } + GB_ASSERT(type != nullptr); - Ast *field_arg = unparen_expr(ce->args[1]); if (field_arg == nullptr || field_arg->kind != Ast_Ident) { error(field_arg, "Expected an identifier for field argument"); return false; } if (is_type_array(type)) { - error(field_arg, "Invalid type for 'offset_of'"); + gbString t = type_to_string(type); + error(field_arg, "Invalid a struct type for 'offset_of', got '%s'", t); + gb_string_free(t); return false; } ast_node(arg, Ident, field_arg); - Selection sel = lookup_field(type, arg->token.string, operand->mode == Addressing_Type); + String field_name = arg->token.string; + Selection sel = lookup_field(type, field_name, false); if (sel.entity == nullptr) { - gbString type_str = type_to_string(bt); + gbString type_str = type_to_string(type); error(ce->args[0], "'%s' has no field named '%.*s'", type_str, LIT(arg->token.string)); gb_string_free(type_str); return false; } if (sel.indirect) { - gbString type_str = type_to_string(bt); + gbString type_str = type_to_string(type); error(ce->args[0], "Field '%.*s' is embedded via a pointer in '%s'", LIT(arg->token.string), type_str); gb_string_free(type_str); @@ -486,7 +525,6 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 operand->mode = Addressing_Constant; operand->value = exact_value_i64(type_offset_of_from_selection(type, sel)); operand->type = t_uintptr; - break; } diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index aa93fbacd..57b5d7eb9 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -238,7 +238,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("size_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("align_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, - {STR_LIT("offset_of"), 2, false, Expr_Expr, BuiltinProcPkg_builtin}, + {STR_LIT("offset_of"), 1, true, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("type_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("type_info_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, {STR_LIT("typeid_of"), 1, false, Expr_Expr, BuiltinProcPkg_builtin}, diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index a9a9ad0ac..47ff21ee8 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2132,6 +2132,28 @@ LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) { return nullptr; } +LLVMMetadataRef lb_get_base_scope_metadata(lbModule *m, Scope *scope) { + LLVMMetadataRef found = nullptr; + for (;;) { + if (scope == nullptr) { + return nullptr; + } + if (scope->flags & ScopeFlag_Proc) { + found = lb_get_llvm_metadata(m, scope->procedure_entity); + if (found) { + return found; + } + } + if (scope->flags & ScopeFlag_File) { + found = lb_get_llvm_metadata(m, scope->file); + if (found) { + return found; + } + } + scope = scope->parent; + } +} + LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) { GB_ASSERT(type != nullptr); LLVMMetadataRef found = lb_get_llvm_metadata(m, type); @@ -2147,7 +2169,7 @@ LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) { if (type->Named.type_name != nullptr) { Entity *e = type->Named.type_name; - scope = lb_get_llvm_metadata(m, e->scope); + scope = lb_get_base_scope_metadata(m, e->scope); if (scope != nullptr) { file = LLVMDIScopeGetFile(scope); } @@ -2174,8 +2196,6 @@ LLVMMetadataRef lb_debug_type(lbModule *m, Type *type) { switch (bt->kind) { case Type_Enum: { - LLVMMetadataRef scope = nullptr; - LLVMMetadataRef file = nullptr; unsigned line = 0; unsigned element_count = cast(unsigned)bt->Enum.fields.count; LLVMMetadataRef *elements = gb_alloc_array(permanent_allocator(), LLVMMetadataRef, element_count); @@ -13460,7 +13480,6 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) { default: GB_PANIC("Unknown CompoundLit type: %s", type_to_string(type)); break; case Type_Struct: { - // TODO(bill): "constant" '#raw_union's are not initialized constantly at the moment. // NOTE(bill): This is due to the layout of the unions when printed to LLVM-IR bool is_raw_union = is_type_raw_union(bt); @@ -15048,9 +15067,23 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start GB_ASSERT(e->kind == Entity_Variable); e->code_gen_module = entity_module; - if (var->decl->init_expr != nullptr) { - // gb_printf_err("%s\n", expr_to_string(var->decl->init_expr)); - lbValue init = lb_build_expr(p, var->decl->init_expr); + Ast *init_expr = var->decl->init_expr; + if (init_expr != nullptr) { + lbValue init = lb_build_expr(p, init_expr); + if (init.value == nullptr) { + LLVMTypeRef global_type = LLVMGetElementType(LLVMTypeOf(var->var.value)); + if (is_type_untyped_undef(init.type)) { + LLVMSetInitializer(var->var.value, LLVMGetUndef(global_type)); + var->is_initialized = true; + continue; + } else if (is_type_untyped_nil(init.type)) { + LLVMSetInitializer(var->var.value, LLVMConstNull(global_type)); + var->is_initialized = true; + continue; + } + GB_PANIC("Invalid init value, got %s", expr_to_string(init_expr)); + } + LLVMValueKind value_kind = LLVMGetValueKind(init.value); // gb_printf_err("%s %d\n", LLVMPrintValueToString(init.value)); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 826fccc04..edffb7446 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -1409,14 +1409,14 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) { if (t->curr_rune == '=') { advance_to_next_rune(t); token->kind = Token_SubEq; - } else if (t->curr_rune == '-' && peek_byte(t) == '-') { - advance_to_next_rune(t); - advance_to_next_rune(t); - token->kind = Token_Undef; } else if (t->curr_rune == '-') { + insert_semicolon = true; advance_to_next_rune(t); token->kind = Token_Decrement; - insert_semicolon = true; + if (t->curr_rune == '-') { + advance_to_next_rune(t); + token->kind = Token_Undef; + } } else if (t->curr_rune == '>') { advance_to_next_rune(t); token->kind = Token_ArrowRight;