diff --git a/core/bytes/buffer.odin b/core/bytes/buffer.odin index 61fde9605..95b816eac 100644 --- a/core/bytes/buffer.odin +++ b/core/bytes/buffer.odin @@ -8,6 +8,8 @@ MIN_READ :: 512; @(private) SMALL_BUFFER_SIZE :: 64; +// A Buffer is a variable-sized buffer of bytes with a io.Stream interface +// The zero value for Buffer is an empty buffer ready to use. Buffer :: struct { buf: [dynamic]byte, off: int, @@ -35,6 +37,11 @@ buffer_init_string :: proc(b: ^Buffer, s: string) { copy(b.buf[:], s); } +buffer_init_allocator :: proc(b: ^Buffer, len, cap: int, allocator := context.allocator) { + b.buf.allocator = allocator; + reserve(&b.buf, cap); + resize(&b.buf, len); +} buffer_destroy :: proc(b: ^Buffer) { delete(b.buf); @@ -276,6 +283,51 @@ buffer_read_string :: proc(b: ^Buffer, delim: byte) -> (line: string, err: io.Er return string(slice), err; } +buffer_write_to :: proc(b: ^Buffer, w: io.Writer) -> (n: i64, err: io.Error) { + b.last_read = .Invalid; + if byte_count := buffer_length(b); byte_count > 0 { + m, e := io.write(w, b.buf[b.off:]); + if m > byte_count { + panic("bytes.buffer_write_to: invalid io.write count"); + } + b.off += m; + n = i64(m); + if e != nil { + err = e; + return; + } + if m != byte_count { + err = .Short_Write; + return; + } + } + buffer_reset(b); + return; +} + +buffer_read_from :: proc(b: ^Buffer, r: io.Reader) -> (n: i64, err: io.Error) #no_bounds_check { + b.last_read = .Invalid; + for { + i := _buffer_grow(b, MIN_READ); + resize(&b.buf, i); + m, e := io.read(r, b.buf[i:cap(b.buf)]); + if m < 0 { + err = .Negative_Read; + return; + } + + resize(&b.buf, i+m); + n += i64(m); + if e == .EOF { + return; + } + if e != nil { + err = e; + return; + } + } + return; +} buffer_to_stream :: proc(b: ^Buffer) -> (s: io.Stream) { @@ -327,9 +379,13 @@ _buffer_vtable := &io.Stream_VTable{ buffer_destroy(b); return nil; }, - - // TODO(bill): write_to and read_from - // impl_write_to = nil, - // impl_read_from = nil, + impl_write_to = proc(s: io.Stream, w: io.Writer) -> (n: i64, err: io.Error) { + b := (^Buffer)(s.stream_data); + return buffer_write_to(b, w); + }, + impl_read_from = proc(s: io.Stream, r: io.Reader) -> (n: i64, err: io.Error) { + b := (^Buffer)(s.stream_data); + return buffer_read_from(b, r); + }, }; diff --git a/core/bytes/strings.odin b/core/bytes/bytes.odin similarity index 90% rename from core/bytes/strings.odin rename to core/bytes/bytes.odin index 61ae4631a..de640421f 100644 --- a/core/bytes/strings.odin +++ b/core/bytes/bytes.odin @@ -743,13 +743,12 @@ split_multi :: proc(s: []byte, substrs: [][]byte, skip_empty := false, allocator return buf; } -/* // scrub scruvs invalid utf-8 characters and replaces them with the replacement string // Adjacent invalid bytes are only replaced once scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> []byte { str := s; - b: Builder; - init_builder(&b, 0, len(s), allocator); + b: Buffer; + buffer_init_allocator(&b, 0, len(s), allocator); has_error := false; cursor := 0; @@ -761,11 +760,11 @@ scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> if r == utf8.RUNE_ERROR { if !has_error { has_error = true; - write(&b, origin[:cursor]); + buffer_write(&b, origin[:cursor]); } } else if has_error { has_error = false; - write(&b, replacement); + buffer_write(&b, replacement); origin = origin[cursor:]; cursor = 0; @@ -775,9 +774,8 @@ scrub :: proc(s: []byte, replacement: []byte, allocator := context.allocator) -> str = str[w:]; } - return to_string(b); + return buffer_to_bytes(&b); } -*/ reverse :: proc(s: []byte, allocator := context.allocator) -> []byte { @@ -795,8 +793,7 @@ reverse :: proc(s: []byte, allocator := context.allocator) -> []byte { return buf; } -/* -expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> string { +expand_tabs :: proc(s: []byte, tab_size: int, allocator := context.allocator) -> []byte { if tab_size <= 0 { panic("tab size must be positive"); } @@ -806,20 +803,20 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> return nil; } - b: Builder; - init_builder(&b, allocator); - writer := to_writer(&b); + b: Buffer; + buffer_init_allocator(&b, 0, len(s), allocator); + str := s; column: int; for len(str) > 0 { - r, w := utf8.decode_rune_in_string(str); + r, w := utf8.decode_rune(str); if r == '\t' { expand := tab_size - column%tab_size; for i := 0; i < expand; i += 1 { - io.write_byte(writer, ' '); + buffer_write_byte(&b, ' '); } column += expand; @@ -830,15 +827,14 @@ expand_tabs :: proc(s: string, tab_size: int, allocator := context.allocator) -> column += w; } - io.write_rune(writer, r); + buffer_write_rune(&b, r); } str = str[w:]; } - return to_string(b); + return buffer_to_bytes(&b); } -*/ partition :: proc(str, sep: []byte) -> (head, match, tail: []byte) { i := index(str, sep); @@ -853,11 +849,10 @@ partition :: proc(str, sep: []byte) -> (head, match, tail: []byte) { return; } -/* center_justify :: centre_justify; // NOTE(bill): Because Americans exist -// centre_justify returns a string with a pad string at boths sides if the str's rune length is smaller than length -centre_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { +// centre_justify returns a byte slice with a pad byte slice at boths sides if the str's rune length is smaller than length +centre_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator) -> []byte { n := rune_count(str); if n >= length || pad == nil { return clone(str, allocator); @@ -866,21 +861,18 @@ centre_justify :: proc(str: string, length: int, pad: string, allocator := conte remains := length-1; pad_len := rune_count(pad); - b: Builder; - init_builder(&b, allocator); - grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); + b: Buffer; + buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator); - w := to_writer(&b); + write_pad_string(&b, pad, pad_len, remains/2); + buffer_write(&b, str); + write_pad_string(&b, pad, pad_len, (remains+1)/2); - write_pad_string(w, pad, pad_len, remains/2); - io.write_string(w, str); - write_pad_string(w, pad, pad_len, (remains+1)/2); - - return to_string(b); + return buffer_to_bytes(&b); } -// left_justify returns a string with a pad string at left side if the str's rune length is smaller than length -left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { +// left_justify returns a byte slice with a pad byte slice at left side if the str's rune length is smaller than length +left_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator) -> []byte { n := rune_count(str); if n >= length || pad == nil { return clone(str, allocator); @@ -889,20 +881,17 @@ left_justify :: proc(str: string, length: int, pad: string, allocator := context remains := length-1; pad_len := rune_count(pad); - b: Builder; - init_builder(&b, allocator); - grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); + b: Buffer; + buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator); - w := to_writer(&b); + buffer_write(&b, str); + write_pad_string(&b, pad, pad_len, remains); - io.write_string(w, str); - write_pad_string(w, pad, pad_len, remains); - - return to_string(b); + return buffer_to_bytes(&b); } -// right_justify returns a string with a pad string at right side if the str's rune length is smaller than length -right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string { +// right_justify returns a byte slice with a pad byte slice at right side if the str's rune length is smaller than length +right_justify :: proc(str: []byte, length: int, pad: []byte, allocator := context.allocator) -> []byte { n := rune_count(str); if n >= length || pad == nil { return clone(str, allocator); @@ -911,39 +900,35 @@ right_justify :: proc(str: string, length: int, pad: string, allocator := contex remains := length-1; pad_len := rune_count(pad); - b: Builder; - init_builder(&b, allocator); - grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad)); + b: Buffer; + buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator); - w := to_writer(&b); + write_pad_string(&b, pad, pad_len, remains); + buffer_write(&b, str); - write_pad_string(w, pad, pad_len, remains); - io.write_string(w, str); - - return to_string(b); + return buffer_to_bytes(&b); } @private -write_pad_string :: proc(w: io.Writer, pad: string, pad_len, remains: int) { +write_pad_string :: proc(b: ^Buffer, pad: []byte, pad_len, remains: int) { repeats := remains / pad_len; for i := 0; i < repeats; i += 1 { - io.write_string(w, pad); + buffer_write(b, pad); } n := remains % pad_len; p := pad; for i := 0; i < n; i += 1 { - r, width := utf8.decode_rune_in_string(p); - io.write_rune(w, r); + r, width := utf8.decode_rune(p); + buffer_write_rune(b, r); p = p[width:]; } } -*/ // fields splits the byte slice s around each instance of one or more consecutive white space character, defined by unicode.is_space diff --git a/core/container/map.odin b/core/container/map.odin index 3e6c18fba..53edae3d5 100644 --- a/core/container/map.odin +++ b/core/container/map.odin @@ -1,14 +1,18 @@ package container +import "intrinsics" +_ :: intrinsics; -Map :: struct(Value: typeid) { + +Map :: struct(Key, Value: typeid) where intrinsics.type_is_valid_map_key(Key) { hash: Array(int), - entries: Array(Map_Entry(Value)), + entries: Array(Map_Entry(Key, Value)), } -Map_Entry :: struct(Value: typeid) { - key: u64, +Map_Entry :: struct(Key, Value: typeid) where intrinsics.type_is_valid_map_key(Key) { + hash: uintptr, next: int, + key: Key, value: Value, } @@ -47,28 +51,28 @@ multi_map_remove_all map_init :: proc{map_init_none, map_init_cap}; -map_init_none :: proc(m: ^$M/Map($Value), allocator := context.allocator) { +map_init_none :: proc(m: ^$M/Map($Key, $Value), allocator := context.allocator) { m.hash.allocator = allocator; m.entries.allocator = allocator; } -map_init_cap :: proc(m: ^$M/Map($Value), cap: int, allocator := context.allocator) { +map_init_cap :: proc(m: ^$M/Map($Key, $Value), cap: int, allocator := context.allocator) { m.hash.allocator = allocator; m.entries.allocator = allocator; map_reserve(m, cap); } -map_delete :: proc(m: $M/Map($Value)) { +map_delete :: proc(m: $M/Map($Key, $Value)) { array_delete(m.hash); array_delete(m.entries); } -map_has :: proc(m: $M/Map($Value), key: u64) -> bool { +map_has :: proc(m: $M/Map($Key, $Value), key: Key) -> bool { return _map_find_or_fail(m, key) >= 0; } -map_get :: proc(m: $M/Map($Value), key: u64) -> (res: Value, ok: bool) #optional_ok { +map_get :: proc(m: $M/Map($Key, $Value), key: Key) -> (res: Value, ok: bool) #optional_ok { i := _map_find_or_fail(m, key); if i < 0 { return {}, false; @@ -76,7 +80,7 @@ map_get :: proc(m: $M/Map($Value), key: u64) -> (res: Value, ok: bool) #optional return array_get(m.entries, i).value, true; } -map_get_default :: proc(m: $M/Map($Value), key: u64, default: Value) -> (res: Value, ok: bool) #optional_ok { +map_get_default :: proc(m: $M/Map($Key, $Value), key: Key, default: Value) -> (res: Value, ok: bool) #optional_ok { i := _map_find_or_fail(m, key); if i < 0 { return default, false; @@ -84,7 +88,7 @@ map_get_default :: proc(m: $M/Map($Value), key: u64, default: Value) -> (res: Va return array_get(m.entries, i).value, true; } -map_get_ptr :: proc(m: $M/Map($Value), key: u64) -> ^Value { +map_get_ptr :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Value { i := _map_find_or_fail(m, key); if i < 0 { return nil; @@ -92,7 +96,7 @@ map_get_ptr :: proc(m: $M/Map($Value), key: u64) -> ^Value { return array_get_ptr(m.entries, i).value; } -map_set :: proc(m: ^$M/Map($Value), key: u64, value: Value) { +map_set :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) { if array_len(m.hash) == 0 { _map_grow(m); } @@ -104,7 +108,7 @@ map_set :: proc(m: ^$M/Map($Value), key: u64, value: Value) { } } -map_remove :: proc(m: ^$M/Map($Value), key: u64) { +map_remove :: proc(m: ^$M/Map($Key, $Value), key: Key) { fr := _map_find_key(m^, key); if fr.entry_index >= 0 { _map_erase(m, fr); @@ -112,7 +116,7 @@ map_remove :: proc(m: ^$M/Map($Value), key: u64) { } -map_reserve :: proc(m: ^$M/Map($Value), new_size: int) { +map_reserve :: proc(m: ^$M/Map($Key, $Value), new_size: int) { nm: M; map_init(&nm, m.hash.allocator); array_resize(&nm.hash, new_size); @@ -130,14 +134,14 @@ map_reserve :: proc(m: ^$M/Map($Value), new_size: int) { m^ = nm; } -map_clear :: proc(m: ^$M/Map($Value)) { +map_clear :: proc(m: ^$M/Map($Key, $Value)) { array_clear(&m.hash); array_clear(&m.entries); } -multi_map_find_first :: proc(m: $M/Map($Value), key: u64) -> ^Map_Entry(Value) { +multi_map_find_first :: proc(m: $M/Map($Key, $Value), key: Key) -> ^Map_Entry(Value) { i := _map_find_or_fail(m, key); if i < 0 { return nil; @@ -145,11 +149,11 @@ multi_map_find_first :: proc(m: $M/Map($Value), key: u64) -> ^Map_Entry(Value) { return array_get_ptr(m.entries, i); } -multi_map_find_next :: proc(m: $M/Map($Value), e: ^Map_Entry(Value)) -> ^Map_Entry(Value) { +multi_map_find_next :: proc(m: $M/Map($Key, $Value), e: ^Map_Entry(Value)) -> ^Map_Entry(Value) { i := e.next; for i >= 0 { it := array_get_ptr(m.entries, i); - if it.key == e.key { + if it.hash == e.hash && it.key == e.key { return it; } i = it.next; @@ -157,7 +161,7 @@ multi_map_find_next :: proc(m: $M/Map($Value), e: ^Map_Entry(Value)) -> ^Map_Ent return nil; } -multi_map_count :: proc(m: $M/Map($Value), key: u64) -> int { +multi_map_count :: proc(m: $M/Map($Key, $Value), key: Key) -> int { n := 0; e := multi_map_find_first(m, key); for e != nil { @@ -169,7 +173,7 @@ multi_map_count :: proc(m: $M/Map($Value), key: u64) -> int { multi_map_get :: proc{multi_map_get_array, multi_map_get_slice}; -multi_map_get_array :: proc(m: $M/Map($Value), key: u64, items: ^Array(Value)) { +multi_map_get_array :: proc(m: $M/Map($Key, $Value), key: Key, items: ^Array(Value)) { if items == nil { return; } @@ -180,7 +184,7 @@ multi_map_get_array :: proc(m: $M/Map($Value), key: u64, items: ^Array(Value)) { } } -multi_map_get_slice :: proc(m: $M/Map($Value), key: u64, items: []Value) { +multi_map_get_slice :: proc(m: $M/Map($Key, $Value), key: Key, items: []Value) { e := multi_map_find_first(m, key); i := 0; for e != nil && i < len(items) { @@ -190,7 +194,7 @@ multi_map_get_slice :: proc(m: $M/Map($Value), key: u64, items: []Value) { } } -multi_map_get_as_slice :: proc(m: $M/Map($Value), key: u64) -> []Value { +multi_map_get_as_slice :: proc(m: $M/Map($Key, $Value), key: Key) -> []Value { items: Array(Value); array_init(&items, 0); @@ -204,7 +208,7 @@ multi_map_get_as_slice :: proc(m: $M/Map($Value), key: u64) -> []Value { } -multi_map_insert :: proc(m: ^$M/Map($Value), key: u64, value: Value) { +multi_map_insert :: proc(m: ^$M/Map($Key, $Value), key: Key, value: Value) { if array_len(m.hash) == 0 { _map_grow(m); } @@ -216,14 +220,14 @@ multi_map_insert :: proc(m: ^$M/Map($Value), key: u64, value: Value) { } } -multi_map_remove :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) { +multi_map_remove :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Value)) { fr := _map_find_entry(m, e); if fr.entry_index >= 0 { _map_erase(m, fr); } } -multi_map_remove_all :: proc(m: ^$M/Map($Value), key: u64) { +multi_map_remove_all :: proc(m: ^$M/Map($Key, $Value), key: Key) { for map_exist(m^, key) { map_remove(m, key); } @@ -239,9 +243,12 @@ Map_Find_Result :: struct { entry_index: int, } -_map_add_entry :: proc(m: ^$M/Map($Value), key: u64) -> int { - e: Map_Entry(Value); +_map_add_entry :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int where intrinsics.type_is_valid_map_key(Key) { + hasher := intrinsics.type_hasher_proc(Key); + + e: Map_Entry(Key, Value); e.key = key; + e.hash = hasher(&e.key, 0); e.next = -1; idx := array_len(m.entries); array_push(&m.entries, e); @@ -271,7 +278,7 @@ _map_erase :: proc(m: ^$M/Map, fr: Map_Find_Result) { } -_map_find_key :: proc(m: $M/Map($Value), key: u64) -> Map_Find_Result { +_map_find_key :: proc(m: $M/Map($Key, $Value), key: Key) -> Map_Find_Result where intrinsics.type_is_valid_map_key(Key) { fr: Map_Find_Result; fr.hash_index = -1; fr.entry_prev = -1; @@ -281,11 +288,16 @@ _map_find_key :: proc(m: $M/Map($Value), key: u64) -> Map_Find_Result { return fr; } - fr.hash_index = int(key % u64(array_len(m.hash))); + hasher := intrinsics.type_hasher_proc(Key); + + key := key; + hash := hasher(&key, 0); + + fr.hash_index = int(hash % uintptr(array_len(m.hash))); fr.entry_index = array_get(m.hash, fr.hash_index); for fr.entry_index >= 0 { it := array_get_ptr(m.entries, fr.entry_index); - if it.key == key { + if it.hash == hash && it.key == key { return fr; } fr.entry_prev = fr.entry_index; @@ -294,7 +306,7 @@ _map_find_key :: proc(m: $M/Map($Value), key: u64) -> Map_Find_Result { return fr; } -_map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Result { +_map_find_entry :: proc(m: ^$M/Map($Key, $Value), e: ^Map_Entry(Value)) -> Map_Find_Result { fr: Map_Find_Result; fr.hash_index = -1; fr.entry_prev = -1; @@ -304,7 +316,7 @@ _map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Re return fr; } - fr.hash_index = int(e.key % u64(array_len(m.hash))); + fr.hash_index = int(e.hash % uintptr(array_len(m.hash))); fr.entry_index = array_get(m.hash, fr.hash_index); for fr.entry_index >= 0 { it := array_get_ptr(m.entries, fr.entry_index); @@ -317,10 +329,10 @@ _map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Re return fr; } -_map_find_or_fail :: proc(m: $M/Map($Value), key: u64) -> int { +_map_find_or_fail :: proc(m: $M/Map($Key, $Value), key: Key) -> int { return _map_find_key(m, key).entry_index; } -_map_find_or_make :: proc(m: ^$M/Map($Value), key: u64) -> int { +_map_find_or_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int { fr := _map_find_key(m^, key); if fr.entry_index >= 0 { return fr.entry_index; @@ -336,7 +348,7 @@ _map_find_or_make :: proc(m: ^$M/Map($Value), key: u64) -> int { } -_map_make :: proc(m: ^$M/Map($Value), key: u64) -> int { +_map_make :: proc(m: ^$M/Map($Key, $Value), key: Key) -> int { fr := _map_find_key(m^, key); i := _map_add_entry(m, key); @@ -352,12 +364,12 @@ _map_make :: proc(m: ^$M/Map($Value), key: u64) -> int { } -_map_full :: proc(m: $M/Map($Value)) -> bool { +_map_full :: proc(m: $M/Map($Key, $Value)) -> bool { // TODO(bill): Determine good max load factor return array_len(m.entries) >= (array_len(m.hash) / 4)*3; } -_map_grow :: proc(m: ^$M/Map($Value)) { +_map_grow :: proc(m: ^$M/Map($Key, $Value)) { new_size := array_len(m.entries) * 4 + 7; // TODO(bill): Determine good grow rate map_reserve(m, new_size); } diff --git a/core/fmt/fmt.odin b/core/fmt/fmt.odin index 84cf4f160..1dfa03579 100644 --- a/core/fmt/fmt.odin +++ b/core/fmt/fmt.odin @@ -12,8 +12,6 @@ import "core:time" import "core:unicode/utf8" import "intrinsics" -DEFAULT_BUFFER_SIZE :: #config(FMT_DEFAULT_BUFFER_SIZE, 1<<10); - Info :: struct { minus: bool, plus: bool, @@ -798,7 +796,7 @@ fmt_int :: proc(fi: ^Info, u: u64, is_signed: bool, bit_size: int, verb: rune) { case 'v': _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER); case 'b': _fmt_int(fi, u, 2, is_signed, bit_size, __DIGITS_LOWER); case 'o': _fmt_int(fi, u, 8, is_signed, bit_size, __DIGITS_LOWER); - case 'd': _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER); + case 'i', 'd': _fmt_int(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER); case 'z': _fmt_int(fi, u, 12, is_signed, bit_size, __DIGITS_LOWER); case 'x': _fmt_int(fi, u, 16, is_signed, bit_size, __DIGITS_LOWER); case 'X': _fmt_int(fi, u, 16, is_signed, bit_size, __DIGITS_UPPER); @@ -823,7 +821,7 @@ fmt_int_128 :: proc(fi: ^Info, u: u128, is_signed: bool, bit_size: int, verb: ru case 'v': _fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER); case 'b': _fmt_int_128(fi, u, 2, is_signed, bit_size, __DIGITS_LOWER); case 'o': _fmt_int_128(fi, u, 8, is_signed, bit_size, __DIGITS_LOWER); - case 'd': _fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER); + case 'i', 'd': _fmt_int_128(fi, u, 10, is_signed, bit_size, __DIGITS_LOWER); case 'z': _fmt_int_128(fi, u, 12, is_signed, bit_size, __DIGITS_LOWER); case 'x': _fmt_int_128(fi, u, 16, is_signed, bit_size, __DIGITS_LOWER); case 'X': _fmt_int_128(fi, u, 16, is_signed, bit_size, __DIGITS_UPPER); @@ -1007,7 +1005,7 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) { case 'b': _fmt_int(fi, u, 2, false, 8*size_of(rawptr), __DIGITS_UPPER); case 'o': _fmt_int(fi, u, 8, false, 8*size_of(rawptr), __DIGITS_UPPER); - case 'd': _fmt_int(fi, u, 10, false, 8*size_of(rawptr), __DIGITS_UPPER); + case 'i', 'd': _fmt_int(fi, u, 10, false, 8*size_of(rawptr), __DIGITS_UPPER); case 'x': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER); case 'X': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER); @@ -1072,7 +1070,7 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { case runtime.Type_Info_Enum: switch verb { case: fmt_bad_verb(fi, verb); - case 'd', 'f': + case 'i', 'd', 'f': fmt_arg(fi, any{v.data, runtime.type_info_base(e.base).id}, verb); case 's', 'v': str, ok := enum_value_to_string(v); @@ -1087,6 +1085,8 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) { stored_enum_value_to_string :: proc(enum_type: ^runtime.Type_Info, ev: runtime.Type_Info_Enum_Value, offset: int = 0) -> (string, bool) { et := runtime.type_info_base(enum_type); + ev := ev; + ev += runtime.Type_Info_Enum_Value(offset); #partial switch e in et.variant { case: return "", false; case runtime.Type_Info_Enum: @@ -1111,18 +1111,6 @@ stored_enum_value_to_string :: proc(enum_type: ^runtime.Type_Info, ev: runtime.T return "", false; } -fmt_write_i64 :: proc(w: io.Writer, i: i64, base: int = 10) { - buf: [32]byte; - s := strconv.append_bits(buf[:], u64(i), base, true, 64, strconv.digits, nil); - io.write_string(w, s); -} -fmt_write_u64 :: proc(w: io.Writer, i: u64, base: int) { - buf: [32]byte; - s := strconv.append_bits(buf[:], u64(i), base, false, 64, strconv.digits, nil); - io.write_string(w, s); -} - - fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool { if ti == nil { @@ -1211,7 +1199,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") { } } v := i64(i) + info.lower; - fmt_write_i64(fi.writer, v, 10); + io.write_i64(fi.writer, v, 10); commas += 1; } } @@ -1253,7 +1241,7 @@ fmt_bit_field :: proc(fi: ^Info, v: any, bit_field_name: string = "") { u <<= sa; u >>= sa; - fmt_write_u64(fi.writer, u, 10); + io.write_u64(fi.writer, u, 10); } io.write_byte(fi.writer, '}'); @@ -1312,7 +1300,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) { for in 0.. (n: int, err: Error) { @@ -21,11 +20,9 @@ write_int :: proc(w: Writer, i: int, base: int = 10) -> (n: int, err: Error) { return write_i64(w, i64(i), base); } -@(private) Tee_Reader :: struct { r: Reader, w: Writer, - allocator: runtime.Allocator, } @(private) @@ -40,27 +37,22 @@ _tee_reader_vtable := &Stream_VTable{ } return; }, - impl_destroy = proc(s: Stream) -> Error { - t := (^Tee_Reader)(s.stream_data); - allocator := t.allocator; - free(t, allocator); - return .None; - }, }; -// tee_reader returns a Reader that writes to 'w' what it reads from 'r' +// tee_reader_init returns a Reader that writes to 'w' what it reads from 'r' // All reads from 'r' performed through it are matched with a corresponding write to 'w' // There is no internal buffering done // The write must complete before th read completes // Any error encountered whilst writing is reported as a 'read' error -// tee_reader must call io.destroy when done with -tee_reader :: proc(r: Reader, w: Writer, allocator := context.allocator) -> (out: Reader) { - t := new(Tee_Reader, allocator); +// tee_reader_init must call io.destroy when done with +tee_reader_init :: proc(t: ^Tee_Reader, r: Reader, w: Writer, allocator := context.allocator) -> Reader { t.r, t.w = r, w; - t.allocator = allocator; + return tee_reader_to_reader(t); +} - out.stream_data = t; - out.stream_vtable = _tee_reader_vtable; +tee_reader_to_reader :: proc(t: ^Tee_Reader) -> (r: Reader) { + r.stream_data = t; + r.stream_vtable = _tee_reader_vtable; return; } @@ -90,11 +82,10 @@ _limited_reader_vtable := &Stream_VTable{ }, }; -new_limited_reader :: proc(r: Reader, n: i64) -> ^Limited_Reader { - l := new(Limited_Reader); +limited_reader_init :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader { l.r = r; l.n = n; - return l; + return limited_reader_to_reader(l); } limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) { @@ -103,13 +94,6 @@ limited_reader_to_reader :: proc(l: ^Limited_Reader) -> (r: Reader) { return; } -@(private="package") -inline_limited_reader :: proc(l: ^Limited_Reader, r: Reader, n: i64) -> Reader { - l.r = r; - l.n = n; - return limited_reader_to_reader(l); -} - // Section_Reader implements read, seek, and read_at on a section of an underlying Reader_At Section_Reader :: struct { r: Reader_At, @@ -118,7 +102,7 @@ Section_Reader :: struct { limit: i64, } -init_section_reader :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64) { +section_reader_init :: proc(s: ^Section_Reader, r: Reader_At, off: i64, n: i64) { s.r = r; s.off = off; s.limit = off + n; diff --git a/core/math/math.odin b/core/math/math.odin index ee93a4d8c..ca344afeb 100644 --- a/core/math/math.odin +++ b/core/math/math.odin @@ -508,7 +508,7 @@ factorial :: proc(n: int) -> int { assert(n >= 0, "parameter must not be negative"); assert(n < len(table), "parameter is too large to lookup in the table"); - return 0; + return table[n]; } classify_f32 :: proc(x: f32) -> Float_Class { diff --git a/core/odin/ast/ast.odin b/core/odin/ast/ast.odin index 88a5a1ee3..d51f482a3 100644 --- a/core/odin/ast/ast.odin +++ b/core/odin/ast/ast.odin @@ -35,11 +35,6 @@ Node_State_Flag :: enum { } Node_State_Flags :: distinct bit_set[Node_State_Flag]; - -Comment_Group :: struct { - list: []tokenizer.Token, -} - Node :: struct { pos: tokenizer.Pos, end: tokenizer.Pos, @@ -47,6 +42,54 @@ Node :: struct { derived: any, } +Comment_Group :: struct { + using node: Node, + list: []tokenizer.Token, +} + +Package_Kind :: enum { + Normal, + Runtime, + Init, +} + +Package :: struct { + using node: Node, + kind: Package_Kind, + id: int, + name: string, + fullpath: string, + files: map[string]^File, + + user_data: rawptr, +} + +File :: struct { + using node: Node, + id: int, + pkg: ^Package, + + fullpath: string, + src: []byte, + + docs: ^Comment_Group, + + pkg_decl: ^Package_Decl, + pkg_token: tokenizer.Token, + pkg_name: string, + + decls: [dynamic]^Stmt, + imports: [dynamic]^Import_Decl, + directive_count: int, + + comments: [dynamic]^Comment_Group, + + syntax_warning_count: int, + syntax_error_count: int, +} + + +// Base Types Expr :: struct { using expr_base: Node, diff --git a/core/odin/ast/clone.odin b/core/odin/ast/clone.odin index 155fef1f7..d210d8bd4 100644 --- a/core/odin/ast/clone.odin +++ b/core/odin/ast/clone.odin @@ -67,6 +67,11 @@ clone_node :: proc(node: ^Node) -> ^Node { align = ti.align; } + switch in node.derived { + case Package, File: + panic("Cannot clone this node type"); + } + res := cast(^Node)mem.alloc(size, align); src: rawptr = node; if node.derived != nil { diff --git a/core/odin/ast/file.odin b/core/odin/ast/file.odin deleted file mode 100644 index dafb10e14..000000000 --- a/core/odin/ast/file.odin +++ /dev/null @@ -1,40 +0,0 @@ -package odin_ast - -import "core:odin/tokenizer" - -Package_Kind :: enum { - Normal, - Runtime, - Init, -} - -Package :: struct { - kind: Package_Kind, - id: int, - name: string, - fullpath: string, - files: []^File, - - user_data: rawptr, -} - -File :: struct { - id: int, - pkg: ^Package, - - fullpath: string, - src: []byte, - - pkg_decl: ^Package_Decl, - pkg_token: tokenizer.Token, - pkg_name: string, - - decls: [dynamic]^Stmt, - imports: [dynamic]^Import_Decl, - directive_count: int, - - comments: [dynamic]^Comment_Group, - - syntax_warning_count: int, - syntax_error_count: int, -} diff --git a/core/odin/ast/walk.odin b/core/odin/ast/walk.odin index 09580d9a7..9abdbffdd 100644 --- a/core/odin/ast/walk.odin +++ b/core/odin/ast/walk.odin @@ -59,6 +59,18 @@ walk :: proc(v: ^Visitor, node: ^Node) { } switch n in &node.derived { + case File: + if n.docs != nil { + walk(v, n.docs); + } + walk_stmt_list(v, n.decls[:]); + case Package: + for _, f in n.files { + walk(v, f); + } + + case Comment_Group: + // empty case Bad_Expr: case Ident: case Implicit: @@ -252,29 +264,59 @@ walk :: proc(v: ^Visitor, node: ^Node) { case Bad_Decl: case Value_Decl: + if n.docs != nil { + walk(v, n.docs); + } walk_attribute_list(v, n.attributes[:]); walk_expr_list(v, n.names); if n.type != nil { walk(v, n.type); } walk_expr_list(v, n.values); + if n.comment != nil { + walk(v, n.comment); + } case Package_Decl: + if n.docs != nil { + walk(v, n.docs); + } + if n.comment != nil { + walk(v, n.comment); + } case Import_Decl: + if n.docs != nil { + walk(v, n.docs); + } + if n.comment != nil { + walk(v, n.comment); + } case Foreign_Block_Decl: + if n.docs != nil { + walk(v, n.docs); + } walk_attribute_list(v, n.attributes[:]); if n.foreign_library != nil { walk(v, n.foreign_library); } walk(v, n.body); case Foreign_Import_Decl: + if n.docs != nil { + walk(v, n.docs); + } walk_attribute_list(v, n.attributes[:]); walk(v, n.name); + if n.comment != nil { + walk(v, n.comment); + } case Proc_Group: walk_expr_list(v, n.args); case Attribute: walk_expr_list(v, n.elems); case Field: + if n.docs != nil { + walk(v, n.docs); + } walk_expr_list(v, n.names); if n.type != nil { walk(v, n.type); @@ -282,6 +324,9 @@ walk :: proc(v: ^Visitor, node: ^Node) { if n.default_value != nil { walk(v, n.default_value); } + if n.comment != nil { + walk(v, n.comment); + } case Field_List: for x in n.list { walk(v, x); diff --git a/core/odin/parser/parse_files.odin b/core/odin/parser/parse_files.odin new file mode 100644 index 000000000..99275777c --- /dev/null +++ b/core/odin/parser/parse_files.odin @@ -0,0 +1,89 @@ +package odin_parser + +import "core:odin/tokenizer" +import "core:odin/ast" +import "core:path/filepath" +import "core:fmt" +import "core:os" +import "core:slice" + +collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) { + NO_POS :: tokenizer.Pos{}; + + pkg_path, pkg_path_ok := filepath.abs(path); + if !pkg_path_ok { + return; + } + + path_pattern := fmt.tprintf("%s/*.odin", pkg_path); + matches, err := filepath.glob(path_pattern); + defer delete(matches); + + if err != nil { + return; + } + + pkg = ast.new(ast.Package, NO_POS, NO_POS); + pkg.fullpath = pkg_path; + + for match in matches { + src: []byte; + fullpath, ok := filepath.abs(match); + if !ok { + return; + } + src, ok = os.read_entire_file(fullpath); + if !ok { + delete(fullpath); + return; + } + file := ast.new(ast.File, NO_POS, NO_POS); + file.pkg = pkg; + file.src = src; + file.fullpath = fullpath; + pkg.files[fullpath] = file; + } + + success = true; + return; +} + +parse_package :: proc(pkg: ^ast.Package, p: ^Parser = nil) -> bool { + p := p; + if p == nil { + p = &Parser{}; + p^ = default_parser(); + } + + ok := true; + + files := make([]^ast.File, len(pkg.files), context.temp_allocator); + i := 0; + for _, file in pkg.files { + files[i] = file; + i += 1; + } + slice.sort(files); + + for file in files { + if !parse_file(p, file) { + ok = false; + } + if pkg.name == "" { + pkg.name = file.pkg_decl.name; + } else if pkg.name != file.pkg_decl.name { + error(p, file.pkg_decl.pos, "different package name, expected '%s', got '%s'", pkg.name, file.pkg_decl.name); + } + } + + return ok; +} + +parse_package_from_path :: proc(path: string, p: ^Parser = nil) -> (pkg: ^ast.Package, ok: bool) { + pkg, ok = collect_package(path); + if !ok { + return; + } + ok = parse_package(pkg, p); + return; +} diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 4e6fa1cb4..84835bda9 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -107,6 +107,14 @@ default_parser :: proc() -> Parser { }; } +is_package_name_reserved :: proc(name: string) -> bool { + switch name { + case "builtin", "intrinsics": + return true; + } + return false; +} + parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool { zero_parser: { p.prev_tok = {}; @@ -139,8 +147,11 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool { pkg_name := expect_token_after(p, .Ident, "package"); if pkg_name.kind == .Ident { - if is_blank_ident(pkg_name) { + switch name := pkg_name.text; { + case is_blank_ident(name): error(p, pkg_name.pos, "invalid package name '_'"); + case is_package_name_reserved(name), file.pkg.kind != .Runtime && name == "runtime": + error(p, pkg_name.pos, "use of reserved package name '%s'", name); } } p.file.pkg_name = pkg_name.text; @@ -276,7 +287,7 @@ consume_comment_group :: proc(p: ^Parser, n: int) -> (comments: ^ast.Comment_Gro } if len(list) > 0 { - comments = new(ast.Comment_Group); + comments = ast.new(ast.Comment_Group, list[0].pos, end_pos(list[len(list)-1])); comments.list = list[:]; append(&p.file.comments, comments); } @@ -521,6 +532,7 @@ parse_stmt_list :: proc(p: ^Parser) -> []^ast.Stmt { } parse_block_stmt :: proc(p: ^Parser, is_when: bool) -> ^ast.Stmt { + skip_possible_newline_for_literal(p); if !is_when && p.curr_proc == nil { error(p, p.curr_tok.pos, "you cannot use a block statement in the file scope"); } @@ -546,9 +558,9 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt { body = convert_stmt_to_body(p, parse_stmt(p)); } else { body = parse_block_stmt(p, true); - skip_possible_newline_for_literal(p); } + skip_possible_newline_for_literal(p); if allow_token(p, .Else) { #partial switch p.curr_tok.kind { case .When: @@ -622,11 +634,11 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt { body = convert_stmt_to_body(p, parse_stmt(p)); } else { body = parse_block_stmt(p, false); - skip_possible_newline_for_literal(p); } else_tok := p.curr_tok.pos; + skip_possible_newline_for_literal(p); if allow_token(p, .Else) { #partial switch p.curr_tok.kind { case .If: @@ -687,7 +699,6 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { body = convert_stmt_to_body(p, parse_stmt(p)); } else { body = parse_body(p); - skip_possible_newline_for_literal(p); } range_stmt := ast.new(ast.Range_Stmt, tok.pos, body.end); @@ -722,7 +733,6 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { body = convert_stmt_to_body(p, parse_stmt(p)); } else { body = parse_body(p); - skip_possible_newline_for_literal(p); } @@ -1094,7 +1104,6 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { body = convert_stmt_to_body(p, parse_stmt(p)); } else { body = parse_block_stmt(p, false); - skip_possible_newline_for_literal(p); } if bad_stmt { diff --git a/core/path/filepath/match.odin b/core/path/filepath/match.odin index 75508c449..57307934e 100644 --- a/core/path/filepath/match.odin +++ b/core/path/filepath/match.odin @@ -272,13 +272,13 @@ _glob :: proc(dir, pattern: string, matches: ^[dynamic]string) -> (m: [dynamic]s } defer os.close(d); - fi, ferr := os.fstat(d); + file_info, ferr := os.fstat(d); if ferr != 0 { - os.file_info_delete(fi); + os.file_info_delete(file_info); return; } - if !fi.is_dir { - os.file_info_delete(fi); + if !file_info.is_dir { + os.file_info_delete(file_info); return; } diff --git a/core/path/filepath/path.odin b/core/path/filepath/path.odin index 3a11c09e8..f19a10d46 100644 --- a/core/path/filepath/path.odin +++ b/core/path/filepath/path.odin @@ -2,7 +2,6 @@ // To process paths usch as URLs that depend on forward slashes regardless of the OS, use the path package package filepath -import "core:os" import "core:strings" // is_separator checks whether the byte is a valid separator character @@ -265,7 +264,7 @@ rel :: proc(base_path, target_path: string, allocator := context.allocator) -> ( } buf := make([]byte, size); n := copy(buf, ".."); - for i in 0.. bool { return false; } - path := path[l:]; + path := path; + path = path[l:]; if path == "" { return false; } @@ -123,8 +124,8 @@ split_list :: proc(path: string, allocator := context.allocator) -> []string { } assert(index == count); - for s, i in list { - s, new := strings.replace_all(s, `"`, ``, allocator); + for s0, i in list { + s, new := strings.replace_all(s0, `"`, ``, allocator); if !new { s = strings.clone(s, allocator); } diff --git a/core/strings/intern.odin b/core/strings/intern.odin new file mode 100644 index 000000000..def03d899 --- /dev/null +++ b/core/strings/intern.odin @@ -0,0 +1,54 @@ +package strings + +import "core:mem" + +Intern_Entry :: struct { + len: int, + str: [1]byte, // string is allocated inline with the entry to keep allocations simple +} + +Intern :: struct { + allocator: mem.Allocator, + entries: map[string]^Intern_Entry, +} + +intern_init :: proc(m: ^Intern, allocator := context.allocator, map_allocator := context.allocator) { + m.allocator = allocator; + m.entries = make(map[string]^Intern_Entry, 16, map_allocator); +} + +intern_destroy :: proc(m: ^Intern) { + for _, value in m.entries { + free(value, m.allocator); + } + delete(m.entries); +} + +intern_get :: proc(m: ^Intern, text: string) -> string { + entry := _intern_get_entry(m, text); + return #no_bounds_check string(entry.str[:entry.len]); +} +intern_get_cstring :: proc(m: ^Intern, text: string) -> cstring { + entry := _intern_get_entry(m, text); + return cstring(&entry.str[0]); +} + +_intern_get_entry :: proc(m: ^Intern, text: string) -> ^Intern_Entry #no_bounds_check { + if prev, ok := m.entries[text]; ok { + return prev; + } + if m.allocator.procedure == nil { + m.allocator = context.allocator; + } + + entry_size := int(offset_of(Intern_Entry, str)) + len(text) + 1; + new_entry := (^Intern_Entry)(mem.alloc(entry_size, align_of(Intern_Entry), m.allocator)); + + new_entry.len = len(text); + copy(new_entry.str[:new_entry.len], text); + new_entry.str[new_entry.len] = 0; + + key := string(new_entry.str[:new_entry.len]); + m.entries[key] = new_entry; + return new_entry; +} diff --git a/core/text/scanner/scanner.odin b/core/text/scanner/scanner.odin index 03b4bb28a..45c2a5d8f 100644 --- a/core/text/scanner/scanner.odin +++ b/core/text/scanner/scanner.odin @@ -61,14 +61,17 @@ Scan_Flag :: enum u32 { Scan_Comments, Skip_Comments, // if set with .Scan_Comments, comments become white space } -Scan_Flags :: bit_set[Scan_Flag; u32]; +Scan_Flags :: distinct bit_set[Scan_Flag; u32]; Odin_Like_Tokens :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments}; C_Like_Tokens :: Scan_Flags{.Scan_Idents, .Scan_Ints, .Scan_C_Int_Prefixes, .Scan_Floats, .Scan_Chars, .Scan_Strings, .Scan_Raw_Strings, .Scan_Comments, .Skip_Comments}; +// Only allows for ASCII whitespace +Whitespace :: distinct bit_set['\x00'.. (tok: rune) { s.pos.line = 0; redo: for { - for s.whitespace & (1< { } }: +let + odin-unwrapped = pkgs.llvmPackages_11.stdenv.mkDerivation (rec { + name = "odin-unwrapped"; + src = ./.; + dontConfigure = true; + nativeBuildInputs = [ pkgs.git ]; + buildPhase = '' + make debug SHELL=${pkgs.llvmPackages_11.stdenv.shell} + ''; + installPhase = '' + mkdir -p $out/bin + cp odin $out/bin/odin + cp -r core $out/bin/core + ''; + }); + path = builtins.map (path: path + "/bin") (with pkgs.llvmPackages_11; [ + bintools + llvm + clang + lld + ]); +in +pkgs.writeScriptBin "odin" '' + #!${pkgs.llvmPackages_11.stdenv.shell} + PATH="${(builtins.concatStringsSep ":" path)}" exec ${odin-unwrapped}/bin/odin $@ +'' diff --git a/src/check_decl.cpp b/src/check_decl.cpp index 5234955fb..9f8f460ba 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -168,18 +168,6 @@ void check_init_constant(CheckerContext *ctx, Entity *e, Operand *operand) { return; } -#if 0 - if (!is_type_constant_type(operand->type)) { - gbString type_str = type_to_string(operand->type); - error(operand->expr, "Invalid constant type: '%s'", type_str); - gb_string_free(type_str); - if (e->type == nullptr) { - e->type = t_invalid; - } - return; - } -#endif - if (e->type == nullptr) { // NOTE(bill): type inference e->type = operand->type; } @@ -388,15 +376,7 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, e->flags |= EntityFlag_Visited; if (type_expr) { - Type *t = check_type(ctx, type_expr); - if (!is_type_constant_type(t) && !is_type_proc(t)) { - gbString str = type_to_string(t); - error(type_expr, "Invalid constant type '%s'", str); - gb_string_free(str); - e->type = t_invalid; - return; - } - e->type = t; + e->type = check_type(ctx, type_expr); } Operand operand = {}; diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 15380e55c..63b275c99 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -7910,7 +7910,9 @@ void check_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *t) { err_str = "used as a value"; break; case Addressing_Type: - err_str = "is not an expression but a type"; + if (t == nullptr || !is_type_typeid(t)) { + err_str = "is not an expression but a type"; + } break; case Addressing_Builtin: err_str = "must be called"; @@ -8760,8 +8762,10 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type i64 lo = exact_value_to_i64(x.value); i64 hi = exact_value_to_i64(y.value); i64 max_index = hi; - if (op.kind == Token_RangeHalf) { + if (op.kind == Token_RangeHalf) { // ..< (exclusive) hi -= 1; + } else { // .. (inclusive) + max_index += 1; } bool new_range = range_cache_add_range(&rc, lo, hi); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 8a41dd12b..117872848 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -1803,9 +1803,19 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) { if (val0 == nullptr) { gbString s = expr_to_string(operand.expr); gbString t = type_to_string(operand.type); + defer (gb_string_free(s)); + defer (gb_string_free(t)); + error(operand.expr, "Cannot iterate over '%s' of type '%s'", s, t); - gb_string_free(t); - gb_string_free(s); + + if (rs->val0 != nullptr && rs->val1 == nullptr) { + if (is_type_map(operand.type) || is_type_bit_set(operand.type)) { + gbString v = expr_to_string(rs->val0); + defer (gb_string_free(v)); + error_line("\tSuggestion: place parentheses around the expression\n"); + error_line("\t for (%s in %s) {\n", v, s); + } + } } } diff --git a/src/ir.cpp b/src/ir.cpp index f69b25494..90cce3699 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -1638,6 +1638,7 @@ irValue *ir_check_compound_lit_constant(irModule *m, Ast *expr) { if (expr == nullptr) { return nullptr; } + if (expr->kind == Ast_CompoundLit) { ast_node(cl, CompoundLit, expr); for_array(i, cl->elems) { @@ -1661,6 +1662,7 @@ irValue *ir_check_compound_lit_constant(irModule *m, Ast *expr) { } } + return nullptr; } @@ -6743,7 +6745,10 @@ irValue *ir_type_info(irProcedure *proc, Type *type) { return ir_emit_array_ep(proc, ir_global_type_info_data, ir_const_i32(id)); } -irValue *ir_typeid(irModule *m, Type *type) { +u64 ir_typeid_as_integer(irModule *m, Type *type) { + if (type == nullptr) { + return 0; + } type = default_type(type); u64 id = cast(u64)ir_type_info_index(m->info, type); @@ -6808,8 +6813,11 @@ irValue *ir_typeid(irModule *m, Type *type) { data |= (reserved &~ (1ull<<1)) << 63ull; // kind } + return id; +} - return ir_value_constant(t_typeid, exact_value_u64(data)); +irValue *ir_typeid(irModule *m, Type *type) { + return ir_value_constant(t_typeid, exact_value_u64(ir_typeid_as_integer(m, type))); } diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a58ddbe0f..594cc57c2 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -787,6 +787,11 @@ void ir_print_exact_value(irFileBuffer *f, irModule *m, ExactValue value, Type * } switch (value.kind) { + case ExactValue_Typeid: + GB_ASSERT(is_type_typeid(type)); + ir_write_u64(f, ir_typeid_as_integer(m, value.value_typeid)); + break; + case ExactValue_Bool: if (value.value_bool) { ir_write_string(f, are_types_identical(type, t_llvm_bool) ? str_lit("true") : str_lit("1")); diff --git a/src/parser.cpp b/src/parser.cpp index 46af47c2d..59d02090e 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1284,8 +1284,7 @@ bool skip_possible_newline(AstFile *f) { if ((f->tokenizer.flags & TokenizerFlag_InsertSemicolon) == 0) { return false; } - Token *prev = &f->curr_token; - if (prev->kind == Token_Semicolon && prev->string == "\n") { + if (token_is_newline(f->curr_token)) { advance_token(f); return true; } @@ -1296,10 +1295,10 @@ bool skip_possible_newline_for_literal(AstFile *f) { if ((f->tokenizer.flags & TokenizerFlag_InsertSemicolon) == 0) { return false; } - TokenPos curr_pos = f->curr_token.pos; - if (token_is_newline(f->curr_token)) { + Token curr = f->curr_token; + if (token_is_newline(curr)) { Token next = peek_token(f); - if (curr_pos.line+1 >= next.pos.line) { + if (curr.pos.line+1 >= next.pos.line) { switch (next.kind) { case Token_OpenBrace: case Token_else: @@ -3182,6 +3181,7 @@ Ast *parse_simple_stmt(AstFile *f, u32 flags) { Ast *parse_block_stmt(AstFile *f, b32 is_when) { + skip_possible_newline_for_literal(f); if (!is_when && f->curr_proc == nullptr) { syntax_error(f->curr_token, "You cannot use a block statement in the file scope"); return ast_bad_stmt(f, f->curr_token, f->curr_token); @@ -3796,9 +3796,9 @@ Ast *parse_if_stmt(AstFile *f) { } } else { body = parse_block_stmt(f, false); - skip_possible_newline_for_literal(f); } + skip_possible_newline_for_literal(f); if (allow_token(f, Token_else)) { switch (f->curr_token.kind) { case Token_if: @@ -3852,9 +3852,9 @@ Ast *parse_when_stmt(AstFile *f) { } } else { body = parse_block_stmt(f, true); - skip_possible_newline_for_literal(f); } + skip_possible_newline_for_literal(f); if (allow_token(f, Token_else)) { switch (f->curr_token.kind) { case Token_when: @@ -3958,7 +3958,6 @@ Ast *parse_for_stmt(AstFile *f) { } } else { body = parse_block_stmt(f, false); - skip_possible_newline_for_literal(f); } return ast_range_stmt(f, token, nullptr, nullptr, in_token, rhs, body); } @@ -3994,7 +3993,6 @@ Ast *parse_for_stmt(AstFile *f) { } } else { body = parse_block_stmt(f, false); - skip_possible_newline_for_literal(f); } if (is_range) { @@ -4346,7 +4344,6 @@ Ast *parse_stmt(AstFile *f) { } } else { body = parse_block_stmt(f, false); - skip_possible_newline_for_literal(f); } if (bad_stmt) { return ast_bad_stmt(f, inline_token, f->curr_token); diff --git a/src/types.cpp b/src/types.cpp index df87cb645..d79a3337a 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1720,6 +1720,9 @@ TypeTuple *get_record_polymorphic_params(Type *t) { bool is_type_polymorphic(Type *t, bool or_specialized=false) { + if (t == nullptr) { + return false; + } if (t->flags & TypeFlag_InProcessOfCheckingPolymorphic) { return false; }