Pārlūkot izejas kodu

Merge pull request #1 from odin-lang/master

update from master
DanielGavin 4 gadi atpakaļ
vecāks
revīzija
bd6ead32f8

+ 60 - 4
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);
+	},
 };
 

+ 39 - 54
core/bytes/strings.odin → 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));
-
-	w := to_writer(&b);
+	b: Buffer;
+	buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator);
 
-	write_pad_string(w, pad, pad_len, remains/2);
-	io.write_string(w, str);
-	write_pad_string(w, pad, pad_len, (remains+1)/2);
+	write_pad_string(&b, pad, pad_len, remains/2);
+	buffer_write(&b, str);
+	write_pad_string(&b, 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));
-
-	w := to_writer(&b);
+	b: Buffer;
+	buffer_init_allocator(&b, 0, len(str) + (remains/pad_len + 1)*len(pad), allocator);
 
-	write_pad_string(w, pad, pad_len, remains);
-	io.write_string(w, str);
+	write_pad_string(&b, pad, pad_len, remains);
+	buffer_write(&b, 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

+ 49 - 37
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);
 }

+ 12 - 24
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 {
 			io.write_byte(fi.writer, '0');
 		}
-		fmt_write_i64(fi.writer, i, 10);
+		io.write_i64(fi.writer, i, 10);
 	}
 
 
@@ -1654,7 +1642,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 				io.write_byte(fi.writer, '.');
 				io.write_string(fi.writer, idx);
 			} else {
-				fmt_write_i64(fi.writer, i64(info.min_value)+i64(i));
+				io.write_i64(fi.writer, i64(info.min_value)+i64(i));
 			}
 			io.write_string(fi.writer, " = ");
 
@@ -2060,9 +2048,9 @@ fmt_arg :: proc(fi: ^Info, arg: any, verb: rune) {
 		if fi.hash && verb == 'v' {
 			io.write_string(fi.writer, a.file_path);
 			io.write_byte(fi.writer, '(');
-			fmt_write_i64(fi.writer, i64(a.line), 10);
+			io.write_i64(fi.writer, i64(a.line), 10);
 			io.write_byte(fi.writer, ':');
-			fmt_write_i64(fi.writer, i64(a.column), 10);
+			io.write_i64(fi.writer, i64(a.column), 10);
 			io.write_byte(fi.writer, ')');
 			return;
 		}

+ 1 - 1
core/io/io.odin

@@ -436,7 +436,7 @@ copy_buffer :: proc(dst: Writer, src: Reader, buf: []byte) -> (written: i64, err
 // It returns the number of bytes copied and the first error that occurred whilst copying, if any.
 // On return, written == n IFF err == nil
 copy_n :: proc(dst: Writer, src: Reader, n: i64) -> (written: i64, err: Error) {
-	nsrc := inline_limited_reader(&Limited_Reader{}, src, n);
+	nsrc := limited_reader_init(&Limited_Reader{}, src, n);
 	written, err = copy(dst, nsrc);
 	if written == n {
 		return n, nil;

+ 11 - 27
core/io/util.odin

@@ -1,6 +1,5 @@
 package io
 
-import "core:runtime"
 import "core:strconv"
 
 write_u64 :: proc(w: Writer, i: u64, base: int = 10) -> (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;

+ 1 - 1
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 {

+ 48 - 5
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,

+ 5 - 0
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 {

+ 0 - 40
core/odin/ast/file.odin

@@ -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,
-}

+ 45 - 0
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);

+ 89 - 0
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;
+}

+ 16 - 7
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 {

+ 4 - 4
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;
 	}
 

+ 1 - 2
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..<seps {
+		for in 0..<seps {
 			buf[n] = SEPARATOR;
 			copy(buf[n+1:], "..");
 			n += 3;

+ 4 - 3
core/path/filepath/path_windows.odin

@@ -41,7 +41,8 @@ is_abs :: proc(path: string) -> 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);
 		}

+ 54 - 0
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;
+}

+ 8 - 5
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'..<utf8.RUNE_SELF; u128];
+
 // Odin_Whitespace is the default value for the Scanner's whitespace field
-Odin_Whitespace :: 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<' ';
-C_Whitespace    :: 1<<'\t' | 1<<'\n' | 1<<'\r' | 1<<'\v' | 1<<'\f' | 1<<' ';
+Odin_Whitespace :: Whitespace{'\t', '\n', '\r', ' '};
+C_Whitespace    :: Whitespace{'\t', '\n', '\r', '\v', '\f', ' '};
 
 
 // Scanner allows for the reading of Unicode characters and tokens from a string
@@ -102,7 +105,7 @@ Scanner :: struct {
 
 	// The whitespace field controls which characters are recognized as white space
 	// This field may be changed by the user at any time during scanning
-	whitespace: u64,
+	whitespace: Whitespace,
 
 	// is_ident_rune is a predicate controlling the characters accepted as the ith rune in an identifier
 	// The valid characters must not conflict with the set of white space characters
@@ -523,7 +526,7 @@ scan :: proc(s: ^Scanner) -> (tok: rune) {
 	s.pos.line = 0;
 
 	redo: for {
-		for s.whitespace & (1<<uint(ch)) != 0 {
+		for (ch < utf8.RUNE_SELF && ch in s.whitespace) {
 			ch = advance(s);
 		}
 

+ 27 - 0
default.nix

@@ -0,0 +1,27 @@
+{ pkgs ? import <nixpkgs> { } }:
+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 $@
+''

+ 1 - 21
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 = {};

+ 6 - 2
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);

+ 12 - 2
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);
+					}
+				}
 			}
 		}
 

+ 10 - 2
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)));
 }
 
 

+ 5 - 0
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"));

+ 7 - 10
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);

+ 3 - 0
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;
 	}