Browse Source

Merge branch 'master' into master

Brendan Punsky 6 years ago
parent
commit
e3d1d1d85c

+ 17 - 8
core/decimal/decimal.odin

@@ -130,10 +130,15 @@ shift_right :: proc(a: ^Decimal, k: uint) {
 }
 
 shift_left :: proc(a: ^Decimal, k: uint) {
-	delta := int(k/4);
+	// NOTE(bill): used to determine buffer size required for the decimal from the binary shift
+	// 'k' means `1<<k` == `2^k` which equates to roundup(k*log10(2)) digits required
+	log10_2 :: 0.301029995663981195213738894724493026768189881462108541310;
+	capacity := int(f64(k)*log10_2 + 1);
 
-	r := a.count;       // read index
-	w := a.count+delta; // write index
+	r := a.count;          // read index
+	w := a.count+capacity; // write index
+
+	d := len(a.digits);
 
 	n: uint;
 	for r -= 1; r >= 0; r -= 1 {
@@ -141,7 +146,7 @@ shift_left :: proc(a: ^Decimal, k: uint) {
 		quo := n/10;
 		rem := n - 10*quo;
 		w -= 1;
-		if w < len(a.digits) {
+		if w < d {
 			a.digits[w] = byte('0' + rem);
 		} else if rem != 0 {
 			a.trunc = true;
@@ -153,7 +158,7 @@ shift_left :: proc(a: ^Decimal, k: uint) {
 		quo := n/10;
 		rem := n - 10*quo;
 		w -= 1;
-		if 0 <= w && w < len(a.digits) {
+		if w < d {
 			a.digits[w] = byte('0' + rem);
 		} else if rem != 0 {
 			a.trunc = true;
@@ -161,9 +166,12 @@ shift_left :: proc(a: ^Decimal, k: uint) {
 		n = quo;
 	}
 
-	a.count += delta;
-	a.count = min(a.count, len(a.digits));
-	a.decimal_point += delta;
+	// NOTE(bill): Remove unused buffer size
+	assert(w >= 0);
+	capacity -= w;
+
+	a.count = min(a.count+capacity, d);
+	a.decimal_point += capacity;
 	trim(a);
 }
 
@@ -253,3 +261,4 @@ rounded_integer :: proc(a: ^Decimal) -> u64 {
 	}
 	return n;
 }
+

+ 18 - 20
core/encoding/cel/cel.odin

@@ -2,8 +2,6 @@ package cel;
 
 import "core:fmt"
 import "core:strconv"
-import "core:os"
-import "core:mem"
 import "core:unicode/utf8"
 import "core:strings"
 
@@ -34,7 +32,7 @@ Parser :: struct {
 
 print_value :: proc(value: Value, pretty := true, indent := 0) {
 	print_indent :: proc(indent: int) {
-		for i in 0..indent-1 do fmt.print("\t");
+		for _ in 0..indent-1 do fmt.print("\t");
 	}
 
 	switch v in value {
@@ -62,16 +60,16 @@ print_value :: proc(value: Value, pretty := true, indent := 0) {
 		if pretty do fmt.println();
 
 		i := 0;
-		for name, value in v {
+		for name, val in v {
 			if pretty {
 				print_indent(indent+1);
 				fmt.printf("%s = ", name);
-				print_value(value, pretty, indent+1);
+				print_value(val, pretty, indent+1);
 				fmt.println(",");
 			} else {
 				if i > 0 do fmt.print(", ");
 				fmt.printf("%s = ", name);
-				print_value(value, pretty, indent+1);
+				print_value(val, pretty, indent+1);
 				i += 1;
 			}
 		}
@@ -155,7 +153,7 @@ destroy :: proc(p: ^Parser) {
 			delete(v);
 
 		case Dict:
-			for key, value in v do destroy_value(value);
+			for _, dv in v do destroy_value(dv);
 			delete(v);
 		}
 	}
@@ -201,11 +199,12 @@ unquote_char :: proc(s: string, quote: byte) -> (r: rune, multiple_bytes: bool,
 		}
 		return -1;
 	}
+	w: int;
 
 	if s[0] == quote && quote == '"' {
 		return;
 	} else if s[0] >= 0x80 {
-		r, w := utf8.decode_rune_in_string(s);
+		r, w = utf8.decode_rune_in_string(s);
 		return r, true, s[w:], true;
 	} else if s[0] != '\\' {
 		return rune(s[0]), false, s[1:], true;
@@ -291,7 +290,6 @@ unquote_string :: proc(p: ^Parser, t: Token) -> (string, bool) {
 		return t.lit, true;
 	}
 	s := t.lit;
-	n := len(s);
 	quote := '"';
 
 	if s == `""` {
@@ -442,7 +440,7 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
 
 	case Kind.Open_Paren:
 		expect_token(p, Kind.Open_Paren);
-		expr, pos := parse_expr(p);
+		expr, _ := parse_expr(p);
 		expect_token(p, Kind.Close_Paren);
 		return expr, tok.pos;
 
@@ -451,7 +449,7 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
 		elems := make([dynamic]Value, 0, 4);
 		for p.curr_token.kind != Kind.Close_Bracket &&
 		    p.curr_token.kind != Kind.EOF {
-			elem, pos := parse_expr(p);
+			elem, _ := parse_expr(p);
 			append(&elems, elem);
 
 			if p.curr_token.kind == Kind.Semicolon && p.curr_token.lit == "\n" {
@@ -481,9 +479,9 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
 			name, ok := unquote_string(p, name_tok);
 			if !ok do error(p, tok.pos, "Unable to unquote string");
 		    expect_token(p, Kind.Assign);
-			elem, pos := parse_expr(p);
+			elem, _ := parse_expr(p);
 
-			if _, ok := dict[name]; ok {
+			if _, ok2 := dict[name]; ok2 {
 				error(p, name_tok.pos, "Previous declaration of %s in this scope", name);
 			} else {
 				dict[name] = elem;
@@ -533,9 +531,9 @@ parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
 			}
 
 		case Kind.Open_Bracket:
-			open := expect_token(p, Kind.Open_Bracket);
+			expect_token(p, Kind.Open_Bracket);
 			index, index_pos := parse_expr(p);
-			close := expect_token(p, Kind.Close_Bracket);
+			expect_token(p, Kind.Close_Bracket);
 
 
 			switch a in operand {
@@ -613,7 +611,7 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
 
 	case Kind.Not:
 		next_token(p);
-		expr, pos := parse_unary_expr(p);
+		expr, _ := parse_unary_expr(p);
 		if v, ok := expr.(bool); ok {
 			return !v, op.pos;
 		}
@@ -757,9 +755,9 @@ parse_binary_expr :: proc(p: ^Parser, prec_in: int) -> (Value, Pos) {
 
 			if op.kind == Kind.Question {
 				cond := expr;
-				x, x_pos := parse_expr(p);
+				x, _ := parse_expr(p);
 				expect_token(p, Kind.Colon);
-				y, y_pos := parse_expr(p);
+				y, _ := parse_expr(p);
 
 				if t, ok := cond.(bool); ok {
 					expr = t ? x : y;
@@ -824,9 +822,9 @@ parse_assignment :: proc(p: ^Parser) -> bool {
 		expect_token(p, Kind.Assign);
 		name, ok := unquote_string(p, tok);
 		if !ok do error(p, tok.pos, "Unable to unquote string");
-		expr, pos := parse_expr(p);
+		expr, _ := parse_expr(p);
 		d := top_dict(p);
-		if _, ok := d[name]; ok {
+		if _, ok2 := d[name]; ok2 {
 			error(p, tok.pos, "Previous declaration of %s", name);
 		} else {
 			d[name] = expr;

+ 5 - 5
core/encoding/cel/token.odin

@@ -322,7 +322,7 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) {
 	}
 
 	if t.curr_rune == '0' {
-		offset := t.offset;
+		offset = t.offset;
 		advance_to_next_rune(t);
 		switch t.curr_rune {
 		case 'b', 'B':
@@ -403,17 +403,17 @@ scan :: proc(t: ^Tokenizer) -> Token {
 			quote := r;
 			tok = String;
 			for {
-				r := t.curr_rune;
-				if r == '\n' || r < 0 {
+				this_r := t.curr_rune;
+				if this_r == '\n' || r < 0 {
 					token_error(t, "String literal not terminated");
 					break;
 				}
 				advance_to_next_rune(t);
-				if r == quote {
+				if this_r == quote {
 					break;
 				}
 				// TODO(bill); Handle properly
-				if r == '\\' && t.curr_rune == quote {
+				if this_r == '\\' && t.curr_rune == quote {
 					advance_to_next_rune(t);
 				}
 			}

+ 125 - 21
core/fmt/fmt.odin

@@ -10,7 +10,7 @@ import "core:strconv"
 import "core:strings"
 
 
-@(private)
+@private
 DEFAULT_BUFFER_SIZE :: 1<<12;
 
 Info :: struct {
@@ -31,6 +31,7 @@ Info :: struct {
 
 	buf: ^strings.Builder,
 	arg: any, // Temporary
+	record_level: int,
 }
 
 fprint :: proc(fd: os.Handle, args: ..any) -> int {
@@ -551,9 +552,6 @@ _pad :: proc(fi: ^Info, s: string) {
 
 fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
 	switch verb {
-	// case 'e', 'E', 'f', 'F', 'g', 'G', 'v':
-	// case 'f', 'F', 'v':
-
 	case 'f', 'F', 'v':
 		prec: int = 3;
 		if fi.prec_set do prec = fi.prec;
@@ -588,6 +586,59 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
 			_pad(fi, str[1:]);
 		}
 
+	case 'e', 'E':
+		prec: int = 3;
+		if fi.prec_set do prec = fi.prec;
+		buf: [386]byte;
+
+		str := strconv.append_float(buf[1:], v, 'e', prec, bit_size);
+		str = string(buf[:len(str)+1]);
+		if str[1] == '+' || str[1] == '-' {
+			str = str[1:];
+		} else {
+			str[0] = '+';
+		}
+
+		if fi.space && !fi.plus && str[0] == '+' {
+			str[0] = ' ';
+		}
+
+		if len(str) > 1 && (str[1] == 'N' || str[1] == 'I') {
+			strings.write_string(fi.buf, str);
+			return;
+		}
+
+		if fi.plus || str[0] != '+' {
+			if fi.zero && fi.width_set && fi.width > len(str) {
+				strings.write_byte(fi.buf, str[0]);
+				fmt_write_padding(fi, fi.width - len(str));
+				strings.write_string(fi.buf, str[1:]);
+			} else {
+				_pad(fi, str);
+			}
+		} else {
+			_pad(fi, str[1:]);
+		}
+
+	case 'h', 'H':
+		prev_fi := fi^;
+		defer fi^ = prev_fi;
+		fi.hash = false;
+		fi.width = bit_size;
+		fi.zero = true;
+		fi.plus = false;
+
+		u: u64;
+		switch bit_size {
+		case 32: u = u64(transmute(u32)f32(v));
+		case 64: u = transmute(u64)v;
+		case: panic("Unhandled float size");
+		}
+
+		strings.write_string(fi.buf, "0h");
+		_fmt_int(fi, u, 16, false, bit_size, verb == 'h' ? __DIGITS_LOWER : __DIGITS_UPPER);
+
+
 	case:
 		fmt_bad_verb(fi, verb);
 	}
@@ -623,13 +674,20 @@ fmt_cstring :: proc(fi: ^Info, s: cstring, verb: rune) {
 }
 
 fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
+	u := u64(uintptr(p));
 	switch verb {
 	case 'p', 'v':
-		u := u64(uintptr(p));
 		if !fi.hash || verb == 'v' {
 			strings.write_string(fi.buf, "0x");
 		}
 		_fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER);
+
+	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 '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);
+
 	case:
 		fmt_bad_verb(fi, verb);
 	}
@@ -982,6 +1040,43 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 		if v.id == typeid_of(^runtime.Type_Info) {
 			write_type(fi.buf, (^^runtime.Type_Info)(v.data)^);
 		} else {
+			if verb != 'p' {
+				ptr := (^rawptr)(v.data)^;
+				a := any{ptr, info.elem.id};
+
+				elem := runtime.type_info_base(info.elem);
+				if elem != nil do switch e in elem.variant {
+				case runtime.Type_Info_Array,
+				     runtime.Type_Info_Slice,
+				     runtime.Type_Info_Dynamic_Array,
+				     runtime.Type_Info_Map:
+					if ptr == nil {
+						strings.write_string(fi.buf, "<nil>");
+						return;
+					}
+					if fi.record_level < 1 {
+					  	fi.record_level += 1;
+						defer fi.record_level -= 1;
+						strings.write_byte(fi.buf, '&');
+						fmt_value(fi, a, verb);
+						return;
+					}
+
+				case runtime.Type_Info_Struct,
+				     runtime.Type_Info_Union:
+					if ptr == nil {
+						strings.write_string(fi.buf, "<nil>");
+						return;
+					}
+					if fi.record_level < 1 {
+						fi.record_level += 1;
+						defer fi.record_level -= 1;
+						strings.write_byte(fi.buf, '&');
+						fmt_value(fi, a, verb);
+						return;
+					}
+				}
+			}
 			fmt_pointer(fi, (^rawptr)(v.data)^, verb);
 		}
 
@@ -996,14 +1091,19 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 		}
 
 	case runtime.Type_Info_Dynamic_Array:
-		strings.write_byte(fi.buf, '[');
-		defer strings.write_byte(fi.buf, ']');
-		array := cast(^mem.Raw_Dynamic_Array)v.data;
-		for i in 0..array.len-1 {
-			if i > 0 do strings.write_string(fi.buf, ", ");
+		if verb == 'p' {
+			slice := cast(^mem.Raw_Dynamic_Array)v.data;
+			fmt_pointer(fi, slice.data, 'p');
+		} else {
+			strings.write_byte(fi.buf, '[');
+			defer strings.write_byte(fi.buf, ']');
+			array := cast(^mem.Raw_Dynamic_Array)v.data;
+			for i in 0..array.len-1 {
+				if i > 0 do strings.write_string(fi.buf, ", ");
 
-			data := uintptr(array.data) + uintptr(i*info.elem_size);
-			fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
+				data := uintptr(array.data) + uintptr(i*info.elem_size);
+				fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
+			}
 		}
 
 	case runtime.Type_Info_Simd_Vector:
@@ -1021,16 +1121,20 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 
 
 	case runtime.Type_Info_Slice:
-		strings.write_byte(fi.buf, '[');
-		defer strings.write_byte(fi.buf, ']');
-		slice := cast(^mem.Raw_Slice)v.data;
-		for i in 0..slice.len-1 {
-			if i > 0 do strings.write_string(fi.buf, ", ");
+		if verb == 'p' {
+			slice := cast(^mem.Raw_Slice)v.data;
+			fmt_pointer(fi, slice.data, 'p');
+		} else {
+			strings.write_byte(fi.buf, '[');
+			defer strings.write_byte(fi.buf, ']');
+			slice := cast(^mem.Raw_Slice)v.data;
+			for i in 0..slice.len-1 {
+				if i > 0 do strings.write_string(fi.buf, ", ");
 
-			data := uintptr(slice.data) + uintptr(i*info.elem_size);
-			fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
+				data := uintptr(slice.data) + uintptr(i*info.elem_size);
+				fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
+			}
 		}
-
 	case runtime.Type_Info_Map:
 		if verb != 'v' {
 			fmt_bad_verb(fi, verb);
@@ -1161,7 +1265,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
 
 fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) {
 	switch verb {
-	case 'f', 'F', 'v':
+	case 'f', 'F', 'v', 'h', 'H':
 		r, i := real(c), imag(c);
 		fmt_float(fi, r, bits/2, verb);
 		if !fi.plus && i >= 0 {

+ 6 - 6
core/os/os_linux.odin

@@ -212,7 +212,7 @@ get_last_error :: proc() -> int {
 }
 
 open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
-	cstr := strings.new_cstring(path);
+	cstr := strings.clone_to_cstring(path);
 	handle := _unix_open(cstr, flags, mode);
 	delete(cstr);
 	if handle == -1 {
@@ -289,7 +289,7 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
 }
 
 stat :: inline proc(path: string) -> (Stat, Errno) {
-	cstr := strings.new_cstring(path);
+	cstr := strings.clone_to_cstring(path);
 	defer delete(cstr);
 
 	s: Stat;
@@ -310,7 +310,7 @@ fstat :: inline proc(fd: Handle) -> (Stat, Errno) {
 }
 
 access :: inline proc(path: string, mask: int) -> (bool, Errno) {
-	cstr := strings.new_cstring(path);
+	cstr := strings.clone_to_cstring(path);
 	defer delete(cstr);
 	result := _unix_access(cstr, mask);
 	if result == -1 {
@@ -333,7 +333,7 @@ heap_free :: proc(ptr: rawptr) {
 }
 
 getenv :: proc(name: string) -> (string, bool) {
-	path_str := strings.new_cstring(name);
+	path_str := strings.clone_to_cstring(name);
 	defer delete(path_str);
 	cstr := _unix_getenv(path_str);
 	if cstr == nil {
@@ -370,14 +370,14 @@ current_thread_id :: proc "contextless" () -> int {
 }
 
 dlopen :: inline proc(filename: string, flags: int) -> rawptr {
-	cstr := strings.new_cstring(filename);
+	cstr := strings.clone_to_cstring(filename);
 	defer delete(cstr);
 	handle := _unix_dlopen(cstr, flags);
 	return handle;
 }
 dlsym :: inline proc(handle: rawptr, symbol: string) -> rawptr {
 	assert(handle != nil);
-	cstr := strings.new_cstring(symbol);
+	cstr := strings.clone_to_cstring(symbol);
 	defer delete(cstr);
 	proc_handle := _unix_dlsym(handle, cstr);
 	return proc_handle;

+ 75 - 15
core/strconv/strconv.odin

@@ -213,28 +213,28 @@ append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> str
 
 
 
-DecimalSlice :: struct {
+Decimal_Slice :: struct {
 	digits:        []byte,
 	count:         int,
 	decimal_point: int,
 	neg:           bool,
 }
 
-FloatInfo :: struct {
+Float_Info :: struct {
 	mantbits: uint,
 	expbits:  uint,
 	bias:     int,
 }
 
 
-_f16_info := FloatInfo{10, 5,   -15};
-_f32_info := FloatInfo{23, 8,  -127};
-_f64_info := FloatInfo{52, 11, -1023};
+_f16_info := Float_Info{10, 5,   -15};
+_f32_info := Float_Info{23, 8,  -127};
+_f64_info := Float_Info{52, 11, -1023};
 
 
 generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> []byte {
 	bits: u64;
-	flt: ^FloatInfo;
+	flt: ^Float_Info;
 	switch bit_size {
 	case 32:
 		bits = u64(transmute(u32)f32(val));
@@ -276,11 +276,11 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
 	d := &d_;
 	assign(d, mant);
 	shift(d, exp - int(flt.mantbits));
-	digs: DecimalSlice;
+	digs: Decimal_Slice;
 	shortest := prec < 0;
 	if shortest {
 		round_shortest(d, mant, exp, flt);
-		digs = DecimalSlice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
+		digs = Decimal_Slice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
 		switch fmt {
 		case 'e', 'E': prec = digs.count-1;
 		case 'f', 'F': prec = max(digs.count-digs.decimal_point, 0);
@@ -297,14 +297,14 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
 			round(d, prec);
 		}
 
-		digs = DecimalSlice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
+		digs = Decimal_Slice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
 	}
 	return format_digits(buf, shortest, neg, digs, prec, fmt);
 }
 
 
 
-format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: DecimalSlice, prec: int, fmt: byte) -> []byte {
+format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, prec: int, fmt: byte) -> []byte {
 	Buffer :: struct {
 		b: []byte,
 		n: int,
@@ -347,12 +347,72 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: DecimalSlice
 		return to_bytes(b);
 
 	case 'e', 'E':
-		panic("strconv: e/E float printing is not yet supported");
-		return to_bytes(b); // TODO
+		add_bytes(&b, neg ? '-' : '+');
+
+		ch := byte('0');
+		if digs.count != 0 {
+			ch = digs.digits[0];
+		}
+		add_bytes(&b, ch);
+
+		if prec > 0 {
+			add_bytes(&b, '.');
+			i := 1;
+			m := min(digs.count, prec+1);
+			if i < m {
+				add_bytes(&b, ..digs.digits[i:m]);
+				i = m;
+			}
+			for ; i <= prec; i += 1 {
+				add_bytes(&b, '0');
+			}
+		}
+
+		add_bytes(&b, fmt);
+		exp := digs.decimal_point-1;
+		if digs.count == 0 {
+			// Zero has exponent of 0
+			exp = 0;
+		}
+
+		ch = '+';
+		if exp < 0 {
+			ch = '-';
+			exp = -exp;
+		}
+		add_bytes(&b, ch);
+
+		switch {
+		case exp < 10:  add_bytes(&b, '0', byte(exp)+'0'); // add prefix 0
+		case exp < 100: add_bytes(&b, byte(exp/10)+'0',  byte(exp%10)+'0');
+		case:           add_bytes(&b, byte(exp/100)+'0', byte(exp/10)%10+'0', byte(exp%10)+'0');
+		}
+
+		return to_bytes(b);
 
 	case 'g', 'G':
-		panic("strconv: g/G float printing is not yet supported");
-		return to_bytes(b); // TODO
+		eprec := prec;
+		if eprec > digs.count && digs.count >= digs.decimal_point {
+			eprec = digs.count;
+		}
+
+		if shortest {
+			eprec = 6;
+		}
+
+		exp := digs.decimal_point - 1;
+		if exp < -4 || exp >= eprec {
+			if prec > digs.count {
+				prec = digs.count;
+			}
+			return format_digits(buf, shortest, neg, digs, prec-1, fmt+'e'-'g'); // keep the same case
+		}
+
+		if prec > digs.decimal_point {
+			prec = digs.count;
+		}
+
+		return format_digits(buf, shortest, neg, digs, max(prec-digs.decimal_point, 0), 'f');
 
 	case:
 		add_bytes(&b, '%', fmt);
@@ -362,7 +422,7 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: DecimalSlice
 
 }
 
-round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^FloatInfo) {
+round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
 	if mant == 0 { // If mantissa is zero, the number is zero
 		d.count = 0;
 		return;

+ 4 - 0
core/strings/builder.odin

@@ -17,6 +17,10 @@ destroy_builder :: proc(b: ^Builder) {
 	clear(&b.buf);
 }
 
+grow_builder :: proc(b: ^Builder, cap: int) {
+	reserve(&b.buf, cap);
+}
+
 builder_from_slice :: proc(backing: []byte) -> Builder {
 	s := transmute(mem.Raw_Slice)backing;
 	d := mem.Raw_Dynamic_Array{

+ 200 - 1
core/strings/strings.odin

@@ -3,6 +3,21 @@ package strings
 import "core:mem"
 import "core:unicode/utf8"
 
+clone :: proc(s: string, allocator := context.allocator) -> string {
+	c := make([]byte, len(s)+1, allocator);
+	copy(c, cast([]byte)s);
+	c[len(s)] = 0;
+	return string(c[:len(s)]);
+}
+
+clone_to_cstring :: proc(s: string, allocator := context.allocator) -> cstring {
+	c := make([]byte, len(s)+1, allocator);
+	copy(c, cast([]byte)s);
+	c[len(s)] = 0;
+	return cstring(&c[0]);
+}
+
+@(deprecated="Please use 'strings.clone'")
 new_string :: proc(s: string, allocator := context.allocator) -> string {
 	c := make([]byte, len(s)+1, allocator);
 	copy(c, cast([]byte)s);
@@ -10,6 +25,7 @@ new_string :: proc(s: string, allocator := context.allocator) -> string {
 	return string(c[:len(s)]);
 }
 
+@(deprecated="Please use 'strings.clone_to_cstring'")
 new_cstring :: proc(s: string, allocator := context.allocator) -> cstring {
 	c := make([]byte, len(s)+1, allocator);
 	copy(c, cast([]byte)s);
@@ -46,6 +62,10 @@ contains_any :: proc(s, chars: string) -> bool {
 }
 
 
+rune_count :: proc(s: string) -> int {
+	return utf8.rune_count_in_string(s);
+}
+
 
 equal_fold :: proc(s, t: string) -> bool {
 	loop: for s != "" && t != "" {
@@ -209,7 +229,7 @@ last_index_any :: proc(s, chars: string) -> int {
 
 count :: proc(s, substr: string) -> int {
 	if len(substr) == 0 { // special case
-		return utf8.rune_count_in_string(s) + 1;
+		return rune_count(s) + 1;
 	}
 	if len(substr) == 1 {
 		c := substr[0];
@@ -642,3 +662,182 @@ split_multi :: proc(s: string, substrs: []string, 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(str: string, replacement: string, allocator := context.allocator) -> string {
+	b := make_builder(allocator);;
+	grow_builder(&b, len(str));
+
+	has_error := false;
+	cursor := 0;
+	origin := str;
+
+	for len(str) > 0 {
+		r, w := utf8.decode_rune_in_string(str);
+
+		if r == utf8.RUNE_ERROR {
+			if !has_error {
+				has_error = true;
+				write_string(&b, origin[:cursor]);
+			}
+		} else if has_error {
+			has_error = false;
+			write_string(&b, replacement);
+
+			origin = origin[cursor:];
+			cursor = 0;
+		}
+
+		cursor += w;
+		str = str[w:];
+	}
+
+	return to_string(b);
+}
+
+
+reverse :: proc(str: string, allocator := context.allocator) -> string {
+	n := len(str);
+	buf := make([]byte, n);
+	i := 0;
+
+	for len(str) > 0 {
+		_, w := utf8.decode_rune_in_string(str);
+		copy(buf[i:], cast([]byte)str[:w]);
+		str = str[w:];
+	}
+	return string(buf);
+}
+
+expand_tabs :: proc(str: string, tab_size: int, allocator := context.allocator) -> string {
+	if tab_size <= 0 {
+		panic("tab size must be positive");
+	}
+
+	if str == "" {
+		return "";
+	}
+
+	b := make_builder(allocator);
+
+	column: int;
+
+	for len(str) > 0 {
+		r, w := utf8.decode_rune_in_string(str);
+
+		if r == '\t' {
+			expand := tab_size - column%tab_size;
+
+			for i := 0; i < expand; i += 1 {
+				write_byte(&b, ' ');
+			}
+
+			column += expand;
+		} else {
+			if r == '\n' {
+				column = 0;
+			} else {
+				column += w;
+			}
+
+			write_rune(&b, r);
+		}
+
+		str = str[w:];
+	}
+
+	return to_string(b);
+}
+
+
+partition :: proc(str, sep: string) -> (head, match, tail: string) {
+	i := index(str, sep);
+	if i == -1 {
+		head = str;
+		return;
+	}
+
+	head = str[:i];
+	match = str[i:i+len(sep)];
+	tail = str[i+len(sep):];
+	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 {
+	n := rune_count(str);
+	if n >= length || pad == "" {
+		return clone(str, allocator);
+	}
+
+	remains := length-1;
+	pad_len := rune_count(pad);
+
+	b := make_builder(allocator);
+	grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
+
+	write_pad_string(&b, pad, pad_len, remains/2);
+	write_string(&b, str);
+	write_pad_string(&b, pad, pad_len, (remains+1)/2);
+
+	return to_string(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 {
+	n := rune_count(str);
+	if n >= length || pad == "" {
+		return clone(str, allocator);
+	}
+
+	remains := length-1;
+	pad_len := rune_count(pad);
+
+	b := make_builder(allocator);
+	grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
+
+	write_string(&b, str);
+	write_pad_string(&b, pad, pad_len, remains);
+
+	return to_string(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 {
+	n := rune_count(str);
+	if n >= length || pad == "" {
+		return clone(str, allocator);
+	}
+
+	remains := length-1;
+	pad_len := rune_count(pad);
+
+	b := make_builder(allocator);
+	grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
+
+	write_pad_string(&b, pad, pad_len, remains);
+	write_string(&b, str);
+
+	return to_string(b);
+}
+
+
+@private
+write_pad_string :: proc(b: ^Builder, pad: string, pad_len, remains: int) {
+	repeats := remains / pad_len;
+
+	for i := 0; i < repeats; i += 1 {
+		write_string(b, pad);
+	}
+
+	remains = remains % pad_len;
+
+	if remains != 0 do for i := 0; i < remains; i += 1 {
+		r, w := utf8.decode_rune_in_string(pad);
+		write_rune(b, r);
+		pad = pad[w:];
+	}
+}

+ 67 - 79
core/sync/atomic.odin

@@ -11,92 +11,86 @@ Ordering :: enum {
 }
 
 strongest_failure_ordering :: inline proc "contextless" (order: Ordering) -> Ordering {
-	using Ordering;
 	#complete switch order {
-	case Relaxed:                 return Relaxed;
-	case Release:                 return Relaxed;
-	case Acquire:                 return Acquire;
-	case Acquire_Release:         return Acquire;
-	case Sequentially_Consistent: return Sequentially_Consistent;
+	case .Relaxed:                 return .Relaxed;
+	case .Release:                 return .Relaxed;
+	case .Acquire:                 return .Acquire;
+	case .Acquire_Release:         return .Acquire;
+	case .Sequentially_Consistent: return .Sequentially_Consistent;
 	}
-	return Relaxed;
+	return .Relaxed;
 }
 
 fence :: inline proc "contextless" (order: Ordering) {
-	using Ordering;
 	#complete switch order {
-	case Relaxed:                 panic("there is no such thing as a relaxed fence");
-	case Release:                 intrinsics.atomic_fence_rel();
-	case Acquire:                 intrinsics.atomic_fence_acq();
-	case Acquire_Release:         intrinsics.atomic_fence_acqrel();
-	case Sequentially_Consistent: intrinsics.atomic_fence();
+	case .Relaxed:                 panic("there is no such thing as a relaxed fence");
+	case .Release:                 intrinsics.atomic_fence_rel();
+	case .Acquire:                 intrinsics.atomic_fence_acq();
+	case .Acquire_Release:         intrinsics.atomic_fence_acqrel();
+	case .Sequentially_Consistent: intrinsics.atomic_fence();
 	case: panic("unknown order");
 	}
 }
 
 
 atomic_store :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) {
-	using Ordering;
 	#complete switch order {
-	case Relaxed:                 intrinsics.atomic_store_relaxed(dst, val);
-	case Release:                 intrinsics.atomic_store_rel(dst, val);
-	case Sequentially_Consistent: intrinsics.atomic_store(dst, val);
-	case Acquire:         panic("there is not such thing as an acquire store");
-	case Acquire_Release: panic("there is not such thing as an acquire/release store");
+	case .Relaxed:                 intrinsics.atomic_store_relaxed(dst, val);
+	case .Release:                 intrinsics.atomic_store_rel(dst, val);
+	case .Sequentially_Consistent: intrinsics.atomic_store(dst, val);
+	case .Acquire:         panic("there is not such thing as an acquire store");
+	case .Acquire_Release: panic("there is not such thing as an acquire/release store");
 	case: panic("unknown order");
 	}
 }
 
 atomic_load :: inline proc "contextless" (dst: ^$T, order: Ordering) -> T {
-	using Ordering;
 	#complete switch order {
-	case Relaxed:                 return intrinsics.atomic_load_relaxed(dst);
-	case Acquire:                 return intrinsics.atomic_load_acq(dst);
-	case Sequentially_Consistent: return intrinsics.atomic_load(dst);
-	case Release:         panic("there is no such thing as a release load");
-	case Acquire_Release: panic("there is no such thing as an acquire/release load");
+	case .Relaxed:                 return intrinsics.atomic_load_relaxed(dst);
+	case .Acquire:                 return intrinsics.atomic_load_acq(dst);
+	case .Sequentially_Consistent: return intrinsics.atomic_load(dst);
+	case .Release:         panic("there is no such thing as a release load");
+	case .Acquire_Release: panic("there is no such thing as an acquire/release load");
 	}
 	panic("unknown order");
 	return T{};
 }
 
 atomic_swap :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
-	using Ordering;
 	#complete switch order {
-	case Relaxed:                 return intrinsics.atomic_xchg_relaxed(dst, val);
-	case Release:                 return intrinsics.atomic_xchg_rel(dst, val);
-	case Acquire:                 return intrinsics.atomic_xchg_acq(dst, val);
-	case Acquire_Release:         return intrinsics.atomic_xchg_acqrel(dst, val);
-	case Sequentially_Consistent: return intrinsics.atomic_xchg(dst, val);
+	case .Relaxed:                 return intrinsics.atomic_xchg_relaxed(dst, val);
+	case .Release:                 return intrinsics.atomic_xchg_rel(dst, val);
+	case .Acquire:                 return intrinsics.atomic_xchg_acq(dst, val);
+	case .Acquire_Release:         return intrinsics.atomic_xchg_acqrel(dst, val);
+	case .Sequentially_Consistent: return intrinsics.atomic_xchg(dst, val);
 	}
 	panic("unknown order");
 	return T{};
 }
 
 atomic_compare_exchange :: inline proc "contextless" (dst: ^$T, old, new: T, success, failure: Ordering) -> (val: T, ok: bool) {
-	using Ordering;
 	switch failure {
-	case Relaxed:
+	case .Relaxed:
 		switch success {
-		case Relaxed:                 return intrinsics.atomic_cxchg_relaxed(dst, old, new);
-		case Acquire:                 return intrinsics.atomic_cxchg_acq_failrelaxed(dst, old, new);
-		case Acquire_Release:         return intrinsics.atomic_cxchg_acqrel_failrelaxed(dst, old, new);
-		case Sequentially_Consistent: return intrinsics.atomic_cxchg_failrelaxed(dst, old, new);
+		case .Relaxed:                 return intrinsics.atomic_cxchg_relaxed(dst, old, new);
+		case .Acquire:                 return intrinsics.atomic_cxchg_acq_failrelaxed(dst, old, new);
+		case .Acquire_Release:         return intrinsics.atomic_cxchg_acqrel_failrelaxed(dst, old, new);
+		case .Sequentially_Consistent: return intrinsics.atomic_cxchg_failrelaxed(dst, old, new);
 		case: panic("an unknown ordering combination");
 		}
-	case Acquire:
+	case .Acquire:
 		switch success {
-		case Acquire:                 return intrinsics.atomic_cxchg_acq(dst, old, new);
+		case .Acquire:                 return intrinsics.atomic_cxchg_acq(dst, old, new);
 		case: panic("an unknown ordering combination");
 		}
-	case Sequentially_Consistent:
+	case .Sequentially_Consistent:
 		switch success {
-		case Sequentially_Consistent: return intrinsics.atomic_cxchg(dst, old, new);
+		case .Sequentially_Consistent: return intrinsics.atomic_cxchg(dst, old, new);
 		case: panic("an unknown ordering combination");
 		}
-	case Acquire_Release:
+	case .Acquire_Release:
 		panic("there is not such thing as an acquire/release failure ordering");
-	case Release:
+	case .Release:
 		panic("there is not such thing as an release failure ordering");
 	}
 	return T{}, false;
@@ -105,78 +99,72 @@ atomic_compare_exchange :: inline proc "contextless" (dst: ^$T, old, new: T, suc
 
 
 atomic_add :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
-	using Ordering;
 	#complete switch order {
-	case Relaxed:                 return intrinsics.atomic_add_relaxed(dst, val);
-	case Release:                 return intrinsics.atomic_add_rel(dst, val);
-	case Acquire:                 return intrinsics.atomic_add_acq(dst, val);
-	case Acquire_Release:         return intrinsics.atomic_add_acqrel(dst, val);
-	case Sequentially_Consistent: return intrinsics.atomic_add(dst, val);
+	case .Relaxed:                 return intrinsics.atomic_add_relaxed(dst, val);
+	case .Release:                 return intrinsics.atomic_add_rel(dst, val);
+	case .Acquire:                 return intrinsics.atomic_add_acq(dst, val);
+	case .Acquire_Release:         return intrinsics.atomic_add_acqrel(dst, val);
+	case .Sequentially_Consistent: return intrinsics.atomic_add(dst, val);
 	}
 	panic("unknown order");
 	return T{};
 }
 
 atomic_sub :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
-	using Ordering;
 	#complete switch order {
-	case Relaxed:                 return intrinsics.atomic_sub_relaxed(dst, val);
-	case Release:                 return intrinsics.atomic_sub_rel(dst, val);
-	case Acquire:                 return intrinsics.atomic_sub_acq(dst, val);
-	case Acquire_Release:         return intrinsics.atomic_sub_acqrel(dst, val);
-	case Sequentially_Consistent: return intrinsics.atomic_sub(dst, val);
+	case .Relaxed:                 return intrinsics.atomic_sub_relaxed(dst, val);
+	case .Release:                 return intrinsics.atomic_sub_rel(dst, val);
+	case .Acquire:                 return intrinsics.atomic_sub_acq(dst, val);
+	case .Acquire_Release:         return intrinsics.atomic_sub_acqrel(dst, val);
+	case .Sequentially_Consistent: return intrinsics.atomic_sub(dst, val);
 	}
 	panic("unknown order");
 	return T{};
 }
 
 atomic_and :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
-	using Ordering;
 	#complete switch order {
-	case Relaxed:                 return intrinsics.atomic_and_relaxed(dst, val);
-	case Release:                 return intrinsics.atomic_and_rel(dst, val);
-	case Acquire:                 return intrinsics.atomic_and_acq(dst, val);
-	case Acquire_Release:         return intrinsics.atomic_and_acqrel(dst, val);
-	case Sequentially_Consistent: return intrinsics.atomic_and(dst, val);
+	case .Relaxed:                 return intrinsics.atomic_and_relaxed(dst, val);
+	case .Release:                 return intrinsics.atomic_and_rel(dst, val);
+	case .Acquire:                 return intrinsics.atomic_and_acq(dst, val);
+	case .Acquire_Release:         return intrinsics.atomic_and_acqrel(dst, val);
+	case .Sequentially_Consistent: return intrinsics.atomic_and(dst, val);
 	}
 	panic("unknown order");
 	return T{};
 }
 
 atomic_nand :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
-	using Ordering;
 	#complete switch order {
-	case Relaxed:                 return intrinsics.atomic_nand_relaxed(dst, val);
-	case Release:                 return intrinsics.atomic_nand_rel(dst, val);
-	case Acquire:                 return intrinsics.atomic_nand_acq(dst, val);
-	case Acquire_Release:         return intrinsics.atomic_nand_acqrel(dst, val);
-	case Sequentially_Consistent: return intrinsics.atomic_nand(dst, val);
+	case .Relaxed:                 return intrinsics.atomic_nand_relaxed(dst, val);
+	case .Release:                 return intrinsics.atomic_nand_rel(dst, val);
+	case .Acquire:                 return intrinsics.atomic_nand_acq(dst, val);
+	case .Acquire_Release:         return intrinsics.atomic_nand_acqrel(dst, val);
+	case .Sequentially_Consistent: return intrinsics.atomic_nand(dst, val);
 	}
 	panic("unknown order");
 	return T{};
 }
 
 atomic_or :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
-	using Ordering;
 	#complete switch order {
-	case Relaxed:                 return intrinsics.atomic_or_relaxed(dst, val);
-	case Release:                 return intrinsics.atomic_or_rel(dst, val);
-	case Acquire:                 return intrinsics.atomic_or_acq(dst, val);
-	case Acquire_Release:         return intrinsics.atomic_or_acqrel(dst, val);
-	case Sequentially_Consistent: return intrinsics.atomic_or(dst, val);
+	case .Relaxed:                 return intrinsics.atomic_or_relaxed(dst, val);
+	case .Release:                 return intrinsics.atomic_or_rel(dst, val);
+	case .Acquire:                 return intrinsics.atomic_or_acq(dst, val);
+	case .Acquire_Release:         return intrinsics.atomic_or_acqrel(dst, val);
+	case .Sequentially_Consistent: return intrinsics.atomic_or(dst, val);
 	}
 	panic("unknown order");
 	return T{};
 }
 
 atomic_xor :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
-	using Ordering;
 	#complete switch order {
-	case Relaxed:                 return intrinsics.atomic_xor_relaxed(dst, val);
-	case Release:                 return intrinsics.atomic_xor_rel(dst, val);
-	case Acquire:                 return intrinsics.atomic_xor_acq(dst, val);
-	case Acquire_Release:         return intrinsics.atomic_xor_acqrel(dst, val);
-	case Sequentially_Consistent: return intrinsics.atomic_xor(dst, val);
+	case .Relaxed:                 return intrinsics.atomic_xor_relaxed(dst, val);
+	case .Release:                 return intrinsics.atomic_xor_rel(dst, val);
+	case .Acquire:                 return intrinsics.atomic_xor_acq(dst, val);
+	case .Acquire_Release:         return intrinsics.atomic_xor_acqrel(dst, val);
+	case .Sequentially_Consistent: return intrinsics.atomic_xor(dst, val);
 	}
 	panic("unknown order");
 	return T{};

+ 17 - 7
core/sys/win32/kernel32.odin

@@ -5,7 +5,6 @@ foreign import "system:kernel32.lib"
 
 @(default_calling_convention = "std")
 foreign kernel32 {
-	@(link_name="GetLastError")              get_last_error               :: proc() -> i32 ---;
 	@(link_name="CreateProcessA")		     create_process_a		      :: proc(application_name, command_line: cstring,
 	                                                              				 process_attributes, thread_attributes: ^Security_Attributes,
 	                                                              				 inherit_handle: Bool, creation_flags: u32, environment: rawptr,
@@ -18,8 +17,12 @@ foreign kernel32 {
                                                                                  process_information: ^Process_Information) -> Bool ---;
 	@(link_name="GetExitCodeProcess")		 get_exit_code_process        :: proc(process: Handle, exit: ^u32) -> Bool ---;
 	@(link_name="ExitProcess")               exit_process                 :: proc(exit_code: u32) ---;
-	@(link_name="GetModuleHandleA")          get_module_handle_a          :: proc(module_name: cstring) -> Hinstance ---;
-	@(link_name="GetModuleHandleW")          get_module_handle_w          :: proc(module_name: Wstring) -> Hinstance ---;
+	@(link_name="GetModuleHandleA")          get_module_handle_a          :: proc(module_name: cstring) -> Hmodule ---;
+	@(link_name="GetModuleHandleW")          get_module_handle_w          :: proc(module_name: Wstring) -> Hmodule ---;
+
+	@(link_name="GetModuleFileNameA")        get_module_file_name_a       :: proc(module: Hmodule, filename: cstring, size: u32) -> u32 ---;
+	@(link_name="GetModuleFileNameW")        get_module_file_name_w       :: proc(module: Hmodule, filename: Wstring, size: u32) -> u32 ---;
+
 	@(link_name="Sleep")                     sleep                        :: proc(ms: i32) -> i32 ---;
 	@(link_name="QueryPerformanceFrequency") query_performance_frequency  :: proc(result: ^i64) -> i32 ---;
 	@(link_name="QueryPerformanceCounter")   query_performance_counter    :: proc(result: ^i64) -> i32 ---;
@@ -54,10 +57,6 @@ foreign kernel32 {
 	@(link_name="WriteFile") write_file :: proc(h: Handle, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> Bool ---;
 
 	@(link_name="GetFileSizeEx")              get_file_size_ex               :: proc(file_handle: Handle, file_size: ^i64) -> Bool ---;
-	@(link_name="GetFileAttributesA")         get_file_attributes_a          :: proc(filename: cstring) -> u32 ---;
-	@(link_name="GetFileAttributesW")         get_file_attributes_w          :: proc(filename: Wstring) -> u32 ---;
-	@(link_name="GetFileAttributesExA")       get_file_attributes_ex_a       :: proc(filename: cstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool ---;
-	@(link_name="GetFileAttributesExW")       get_file_attributes_ex_w       :: proc(filename: Wstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool ---;
 	@(link_name="GetFileInformationByHandle") get_file_information_by_handle :: proc(file_handle: Handle, file_info: ^By_Handle_File_Information) -> Bool ---;
 
 	@(link_name="CreateDirectoryA") 		  create_directory_a			 :: proc(path: cstring, security_attributes: ^Security_Attributes) -> Bool ---;
@@ -117,6 +116,17 @@ foreign kernel32 {
 	@(link_name="WaitForSingleObject") wait_for_single_object :: proc(handle: Handle, milliseconds: u32) -> u32 ---;
 }
 
+@(default_calling_convention = "c")
+foreign kernel32 {
+	@(link_name="GetLastError")              get_last_error               :: proc() -> i32 ---;
+
+	@(link_name="GetFileAttributesA")         get_file_attributes_a          :: proc(filename: cstring) -> u32 ---;
+	@(link_name="GetFileAttributesW")         get_file_attributes_w          :: proc(filename: Wstring) -> u32 ---;
+	@(link_name="GetFileAttributesExA")       get_file_attributes_ex_a       :: proc(filename: cstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool ---;
+	@(link_name="GetFileAttributesExW")       get_file_attributes_ex_w       :: proc(filename: Wstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool ---;
+	@(link_name="CompareFileTime")            compare_file_time              :: proc(a, b: ^Filetime) -> i32 ---;
+}
+
 @(default_calling_convention = "c")
 foreign kernel32 {
 	@(link_name="InterlockedCompareExchange") interlocked_compare_exchange :: proc(dst: ^i32, exchange, comparand: i32) -> i32 ---;

+ 1 - 0
src/build_settings.cpp

@@ -92,6 +92,7 @@ struct BuildContext {
 
 	String out_filepath;
 	String resource_filepath;
+	String pdb_filepath;
 	bool   has_resource;
 	String opt_flags;
 	String llc_flags;

+ 12 - 2
src/check_decl.cpp

@@ -391,8 +391,18 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
 			case Entity_Procedure:
 			case Entity_LibraryName:
 			case Entity_ImportName:
-				override_entity_in_scope(e, entity);
-				return;
+				{
+					override_entity_in_scope(e, entity);
+
+					DeclInfo *decl = decl_info_of_entity(e);
+					if (decl != nullptr) {
+						if (decl->attributes.count > 0) {
+							error(decl->attributes[0], "Constant alias declarations cannot have attributes");
+						}
+					}
+
+					return;
+				}
 			}
 		}
 	}

+ 2 - 1
src/check_stmt.cpp

@@ -474,10 +474,11 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
 	case Entity_ImportName: {
 		Scope *scope = e->ImportName.scope;
 		for_array(i, scope->elements.entries) {
+			String name = scope->elements.entries[i].key.string;
 			Entity *decl = scope->elements.entries[i].value;
 			if (!is_entity_exported(decl)) continue;
 
-			Entity *found = scope_insert(ctx->scope, decl);
+			Entity *found = scope_insert_with_name(ctx->scope, name, decl);
 			if (found != nullptr) {
 				gbString expr_str = expr_to_string(expr);
 				error(us->token,

+ 2 - 0
src/check_type.cpp

@@ -2507,7 +2507,9 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
 	case_ast_node(et, BitFieldType, e);
 		*type = alloc_type_bit_field();
 		set_base_type(named_type, *type);
+		check_open_scope(ctx, e);
 		check_bit_field_type(ctx, *type, e);
+		check_close_scope(ctx);
 		return true;
 	case_end;
 

+ 53 - 35
src/checker.cpp

@@ -388,8 +388,7 @@ Entity *scope_lookup(Scope *s, String name) {
 
 
 
-Entity *scope_insert(Scope *s, Entity *entity) {
-	String name = entity->token.string;
+Entity *scope_insert_with_name(Scope *s, String name, Entity *entity) {
 	if (name == "") {
 		return nullptr;
 	}
@@ -406,6 +405,11 @@ Entity *scope_insert(Scope *s, Entity *entity) {
 	return nullptr;
 }
 
+Entity *scope_insert(Scope *s, Entity *entity) {
+	String name = entity->token.string;
+	return scope_insert_with_name(s, name, entity);
+}
+
 
 GB_COMPARE_PROC(entity_variable_pos_cmp) {
 	Entity *x = *cast(Entity **)a;
@@ -1023,39 +1027,41 @@ void add_entity_definition(CheckerInfo *i, Ast *identifier, Entity *entity) {
 	array_add(&i->definitions, entity);
 }
 
-bool add_entity(Checker *c, Scope *scope, Ast *identifier, Entity *entity) {
+bool redeclaration_error(String name, Entity *prev, Entity *found) {
+	TokenPos pos = found->token.pos;
+	Entity *up = found->using_parent;
+	if (up != nullptr) {
+		if (pos == up->token.pos) {
+			// NOTE(bill): Error should have been handled already
+			return false;
+		}
+		error(prev->token,
+		      "Redeclaration of '%.*s' in this scope through 'using'\n"
+		      "\tat %.*s(%td:%td)",
+		      LIT(name),
+		      LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column);
+	} else {
+		if (pos == prev->token.pos) {
+			// NOTE(bill): Error should have been handled already
+			return false;
+		}
+		error(prev->token,
+		      "Redeclaration of '%.*s' in this scope\n"
+		      "\tat %.*s(%td:%td)",
+		      LIT(name),
+		      LIT(pos.file), pos.line, pos.column);
+	}
+	return false;
+}
+
+bool add_entity_with_name(Checker *c, Scope *scope, Ast *identifier, Entity *entity, String name) {
 	if (scope == nullptr) {
 		return false;
 	}
-	String name = entity->token.string;
 	if (!is_blank_ident(name)) {
 		Entity *ie = scope_insert(scope, entity);
 		if (ie != nullptr) {
-			TokenPos pos = ie->token.pos;
-			Entity *up = ie->using_parent;
-			if (up != nullptr) {
-				if (pos == up->token.pos) {
-					// NOTE(bill): Error should have been handled already
-					return false;
-				}
-				error(entity->token,
-				      "Redeclaration of '%.*s' in this scope through 'using'\n"
-				      "\tat %.*s(%td:%td)",
-				      LIT(name),
-				      LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column);
-				return false;
-			} else {
-				if (pos == entity->token.pos) {
-					// NOTE(bill): Error should have been handled already
-					return false;
-				}
-				error(entity->token,
-				      "Redeclaration of '%.*s' in this scope\n"
-				      "\tat %.*s(%td:%td)",
-				      LIT(name),
-				      LIT(pos.file), pos.line, pos.column);
-				return false;
-			}
+			return redeclaration_error(name, entity, ie);
 		}
 	}
 	if (identifier != nullptr) {
@@ -1063,6 +1069,9 @@ bool add_entity(Checker *c, Scope *scope, Ast *identifier, Entity *entity) {
 	}
 	return true;
 }
+bool add_entity(Checker *c, Scope *scope, Ast *identifier, Entity *entity) {
+	return add_entity_with_name(c, scope, identifier, entity, entity->token.string);
+}
 
 void add_entity_use(CheckerContext *c, Ast *identifier, Entity *entity) {
 	if (entity == nullptr) {
@@ -2005,7 +2014,7 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 		if (value != nullptr) {
 			Operand o = {};
 			check_expr(c, &o, value);
-			Entity *e = entity_of_ident(o.expr);
+			Entity *e = entity_of_node(o.expr);
 			if (e != nullptr && e->kind == Entity_Procedure) {
 				warning(elem, "'%.*s' is deprecated, please use one of the following instead: 'deferred_none', 'deferred_in', 'deferred_out'", LIT(name));
 				if (ac->deferred_procedure.entity != nullptr) {
@@ -2022,7 +2031,7 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 		if (value != nullptr) {
 			Operand o = {};
 			check_expr(c, &o, value);
-			Entity *e = entity_of_ident(o.expr);
+			Entity *e = entity_of_node(o.expr);
 			if (e != nullptr && e->kind == Entity_Procedure) {
 				ac->deferred_procedure.kind = DeferredProcedure_none;
 				ac->deferred_procedure.entity = e;
@@ -2035,7 +2044,7 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 		if (value != nullptr) {
 			Operand o = {};
 			check_expr(c, &o, value);
-			Entity *e = entity_of_ident(o.expr);
+			Entity *e = entity_of_node(o.expr);
 			if (e != nullptr && e->kind == Entity_Procedure) {
 				if (ac->deferred_procedure.entity != nullptr) {
 					error(elem, "Previous usage of a 'deferred_*' attribute");
@@ -2051,7 +2060,7 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
 		if (value != nullptr) {
 			Operand o = {};
 			check_expr(c, &o, value);
-			Entity *e = entity_of_ident(o.expr);
+			Entity *e = entity_of_node(o.expr);
 			if (e != nullptr && e->kind == Entity_Procedure) {
 				if (ac->deferred_procedure.entity != nullptr) {
 					error(elem, "Previous usage of a 'deferred_*' attribute");
@@ -3050,12 +3059,21 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
 
 		// NOTE(bill): Add imported entities to this file's scope
 		for_array(elem_index, scope->elements.entries) {
+			String name = scope->elements.entries[elem_index].key.string;
 			Entity *e = scope->elements.entries[elem_index].value;
 			if (e->scope == parent_scope) continue;
 
 			if (is_entity_exported(e)) {
-				Entity *prev = scope_lookup(parent_scope, e->token.string);
-				add_entity(ctx->checker, parent_scope, e->identifier, e);
+				Entity *found = scope_lookup_current(parent_scope, name);
+				if (found != nullptr) {
+					// NOTE(bill):
+					// Date: 2019-03-17
+					// The order has to be the other way around as `using` adds the entity into the that
+					// file scope otherwise the error would be the wrong way around
+					redeclaration_error(name, found, e);
+				} else {
+					add_entity_with_name(ctx->checker, parent_scope, e->identifier, e, name);
+				}
 			}
 		}
 	}

+ 9 - 5
src/ir.cpp

@@ -2875,12 +2875,12 @@ irValue *ir_copy_value_to_ptr(irProcedure *proc, irValue *val, Type *new_type, i
 	if (alignment < type_alignment) {
 		alignment = type_alignment;
 	}
+	GB_ASSERT_MSG(are_types_identical(new_type, ir_type(val)), "%s %s", type_to_string(new_type), type_to_string(ir_type(val)));
+
 	irValue *ptr = ir_add_local_generated(proc, new_type, false);
 	ptr->Instr.Local.alignment = alignment;
 	ir_emit_store(proc, ptr, val);
 
-	if (val) val->uses += 1;
-
 	return ptr;
 }
 
@@ -2972,11 +2972,15 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro
 
 		Type *original_type = e->type;
 		Type *new_type = pt->Proc.abi_compat_params[i];
-		if (original_type != new_type) {
-			if (is_type_pointer(new_type)) {
+		Type *arg_type = ir_type(args[i]);
+		if (are_types_identical(arg_type, new_type)) {
+			// NOTE(bill): Done
+		} else if (!are_types_identical(original_type, new_type)) {
+
+			if (is_type_pointer(new_type) && !is_type_pointer(original_type)) {
 				if (e->flags&EntityFlag_Value) {
 					args[i] = ir_address_from_load_or_generate_local(p, args[i]);
-				} else {
+				} else if (!is_type_pointer(arg_type)) {
 					args[i] = ir_copy_value_to_ptr(p, args[i], original_type, 16);
 				}
 			} else if (is_type_integer(new_type)) {

+ 1 - 1
src/ir_print.cpp

@@ -471,7 +471,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
 			i64 align_of_union = type_align_of(t);
 			ir_write_byte(f, '{');
 			ir_print_alignment_prefix_hack(f, align_of_union);
-			ir_fprintf(f, ", [%lld x i8]}", align_of_union, size_of_union);
+			ir_fprintf(f, ", [%lld x i8]}", size_of_union);
 			return;
 		} else {
 			if (t->Struct.is_packed) {

+ 72 - 35
src/main.cpp

@@ -18,7 +18,7 @@
 #include "ir_print.cpp"
 
 // NOTE(bill): 'name' is used in debugging and profiling modes
-i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
+i32 system_exec_command_line_app(char *name, char *fmt, ...) {
 #if defined(GB_SYSTEM_WINDOWS)
 	STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
 	PROCESS_INFORMATION pi = {0};
@@ -201,7 +201,6 @@ enum BuildFlagKind {
 	BuildFlag_Invalid,
 
 	BuildFlag_OutFile,
-	BuildFlag_ResourceFile,
 	BuildFlag_OptimizationLevel,
 	BuildFlag_ShowTimings,
 	BuildFlag_ThreadCount,
@@ -218,6 +217,11 @@ enum BuildFlagKind {
 	BuildFlag_Vet,
 	BuildFlag_IgnoreUnknownAttributes,
 
+#if defined(GB_SYSTEM_WINDOWS)
+	BuildFlag_ResourceFile,
+	BuildFlag_WindowsPdbName,
+#endif
+
 	BuildFlag_COUNT,
 };
 
@@ -281,7 +285,6 @@ ExactValue build_param_to_exact_value(String name, String param) {
 bool parse_build_flags(Array<String> args) {
 	auto build_flags = array_make<BuildFlag>(heap_allocator(), 0, BuildFlag_COUNT);
 	add_flag(&build_flags, BuildFlag_OutFile,           str_lit("out"),             BuildFlagParam_String);
-	add_flag(&build_flags, BuildFlag_ResourceFile,      str_lit("resource"),        BuildFlagParam_String);
 	add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"),             BuildFlagParam_Integer);
 	add_flag(&build_flags, BuildFlag_ShowTimings,       str_lit("show-timings"),    BuildFlagParam_None);
 	add_flag(&build_flags, BuildFlag_ThreadCount,       str_lit("thread-count"),    BuildFlagParam_Integer);
@@ -296,7 +299,12 @@ bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_NoCRT,             str_lit("no-crt"),          BuildFlagParam_None);
 	add_flag(&build_flags, BuildFlag_UseLLD,            str_lit("lld"),             BuildFlagParam_None);
 	add_flag(&build_flags, BuildFlag_Vet,               str_lit("vet"),             BuildFlagParam_None);
-	add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("-ignore-unknown-attributes"), BuildFlagParam_None);
+	add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None);
+
+#if defined(GB_SYSTEM_WINDOWS)
+	add_flag(&build_flags, BuildFlag_ResourceFile,   str_lit("resource"), BuildFlagParam_String);
+	add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String);
+#endif
 
 	GB_ASSERT(args.count >= 3);
 	Array<String> flag_args = array_slice(args, 3, args.count);
@@ -436,24 +444,6 @@ bool parse_build_flags(Array<String> args) {
 							}
 							break;
 						}
-						case BuildFlag_ResourceFile: {
-							GB_ASSERT(value.kind == ExactValue_String);
-							String path = value.value_string;
-							path = string_trim_whitespace(path);
-							if (is_import_path_valid(path)) {
-								if(!string_ends_with(path, str_lit(".rc"))) {
-									gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
-									bad_flags = true;
-									break;
-								}
-								build_context.resource_filepath = substring(path, 0, string_extension_position(path));
-								build_context.has_resource = true;
-							} else {
-								gb_printf_err("Invalid -resource path, got %.*s\n", LIT(path));
-								bad_flags = true;
-							}
-							break;
-						}
 						case BuildFlag_OptimizationLevel:
 							GB_ASSERT(value.kind == ExactValue_Integer);
 							build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer);
@@ -671,6 +661,48 @@ bool parse_build_flags(Array<String> args) {
 						case BuildFlag_IgnoreUnknownAttributes:
 							build_context.ignore_unknown_attributes = true;
 							break;
+
+					#if defined(GB_SYSTEM_WINDOWS)
+						case BuildFlag_ResourceFile: {
+							GB_ASSERT(value.kind == ExactValue_String);
+							String path = value.value_string;
+							path = string_trim_whitespace(path);
+							if (is_import_path_valid(path)) {
+								if(!string_ends_with(path, str_lit(".rc"))) {
+									gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
+									bad_flags = true;
+									break;
+								}
+								build_context.resource_filepath = substring(path, 0, string_extension_position(path));
+								build_context.has_resource = true;
+							} else {
+								gb_printf_err("Invalid -resource path, got %.*s\n", LIT(path));
+								bad_flags = true;
+							}
+							break;
+						}
+						case BuildFlag_WindowsPdbName: {
+							GB_ASSERT(value.kind == ExactValue_String);
+							String path = value.value_string;
+							path = string_trim_whitespace(path);
+							if (is_import_path_valid(path)) {
+								// #if defined(GB_SYSTEM_WINDOWS)
+								// 	String ext = path_extension(path);
+								// 	if (ext != ".pdb") {
+								// 		path = substring(path, 0, string_extension_position(path));
+								// 	}
+								// #endif
+								build_context.pdb_filepath = path;
+							} else {
+								gb_printf_err("Invalid -pdb-name path, got %.*s\n", LIT(path));
+								bad_flags = true;
+							}
+							break;
+
+							break;
+						}
+					#endif
+
 						}
 					}
 
@@ -784,7 +816,7 @@ void remove_temp_files(String output_base) {
 i32 exec_llvm_opt(String output_base) {
 #if defined(GB_SYSTEM_WINDOWS)
 	// For more passes arguments: http://llvm.org/docs/Passes.html
-	return system_exec_command_line_app("llvm-opt", false,
+	return system_exec_command_line_app("llvm-opt",
 		"\"%.*sbin/opt\" \"%.*s.ll\" -o \"%.*s.bc\" %.*s "
 		"",
 		LIT(build_context.ODIN_ROOT),
@@ -793,7 +825,7 @@ i32 exec_llvm_opt(String output_base) {
 #else
 	// NOTE(zangent): This is separate because it seems that LLVM tools are packaged
 	//   with the Windows version, while they will be system-provided on MacOS and GNU/Linux
-	return system_exec_command_line_app("llvm-opt", false,
+	return system_exec_command_line_app("llvm-opt",
 		"opt \"%.*s.ll\" -o \"%.*s.bc\" %.*s "
 		"",
 		LIT(output_base), LIT(output_base),
@@ -804,10 +836,10 @@ i32 exec_llvm_opt(String output_base) {
 i32 exec_llvm_llc(String output_base) {
 #if defined(GB_SYSTEM_WINDOWS)
 	// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
-	return system_exec_command_line_app("llvm-llc", false,
+	return system_exec_command_line_app("llvm-llc",
 		"\"%.*sbin\\llc\" \"%.*s.bc\" -filetype=obj -O%d "
 		"-o \"%.*s.obj\" "
-		"%.*s "
+		"%.*s"
 		"",
 		LIT(build_context.ODIN_ROOT),
 		LIT(output_base),
@@ -817,7 +849,7 @@ i32 exec_llvm_llc(String output_base) {
 #else
 	// NOTE(zangent): Linux / Unix is unfinished and not tested very well.
 	// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
-	return system_exec_command_line_app("llc", false,
+	return system_exec_command_line_app("llc",
 		"llc \"%.*s.bc\" -filetype=obj -relocation-model=pic -O%d "
 		"%.*s "
 		"%s"
@@ -1041,6 +1073,11 @@ int main(int arg_count, char **arg_ptr) {
 		} else {
 			link_settings = gb_string_append_fmt(link_settings, "/ENTRY:mainCRTStartup");
 		}
+
+		if (build_context.pdb_filepath != "") {
+			link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(build_context.pdb_filepath));
+		}
+
 		if (build_context.no_crt) {
 			link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
 		} else {
@@ -1052,7 +1089,7 @@ int main(int arg_count, char **arg_ptr) {
 		}
 		if (!build_context.use_lld) { // msvc
 			if (build_context.has_resource) {
-				exit_code = system_exec_command_line_app("msvc-link", true,
+				exit_code = system_exec_command_line_app("msvc-link",
 					"rc /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
 					LIT(output_base),
 					LIT(build_context.resource_filepath)
@@ -1062,7 +1099,7 @@ int main(int arg_count, char **arg_ptr) {
 					return exit_code;
 				}
 
-				exit_code = system_exec_command_line_app("msvc-link", true,
+				exit_code = system_exec_command_line_app("msvc-link",
 					"link \"%.*s.obj\" \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
 					"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
 					" %.*s "
@@ -1073,7 +1110,7 @@ int main(int arg_count, char **arg_ptr) {
 					link_settings
 				);
 			} else {
-				exit_code = system_exec_command_line_app("msvc-link", true,
+				exit_code = system_exec_command_line_app("msvc-link",
 					"link \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
 					"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
 					" %.*s "
@@ -1085,7 +1122,7 @@ int main(int arg_count, char **arg_ptr) {
 				);
 			}
 		} else { // lld
-			exit_code = system_exec_command_line_app("msvc-link", true,
+			exit_code = system_exec_command_line_app("msvc-link",
 				"\"%.*s\\bin\\lld-link\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
 				"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
 				" %.*s "
@@ -1109,7 +1146,7 @@ int main(int arg_count, char **arg_ptr) {
 		remove_temp_files(output_base);
 
 		if (run_output) {
-			system_exec_command_line_app("odin run", false, "%.*s.exe %.*s", LIT(output_base), LIT(run_args_string));
+			system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(output_base), LIT(run_args_string));
 		}
 	#else
 		timings_start_section(&timings, str_lit("ld-link"));
@@ -1206,7 +1243,7 @@ int main(int arg_count, char **arg_ptr) {
 			}
 		#endif
 
-		exit_code = system_exec_command_line_app("ld-link", true,
+		exit_code = system_exec_command_line_app("ld-link",
 			"%s \"%.*s.o\" -o \"%.*s%.*s\" %s "
 			" %s "
 			" %.*s "
@@ -1235,7 +1272,7 @@ int main(int arg_count, char **arg_ptr) {
 		if (build_context.ODIN_DEBUG) {
 			// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
 			// to the symbols in the object file
-			exit_code = system_exec_command_line_app("dsymutil", true,
+			exit_code = system_exec_command_line_app("dsymutil",
 				"dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext)
 			);
 
@@ -1256,7 +1293,7 @@ int main(int arg_count, char **arg_ptr) {
 			//NOTE(thebirk): This whole thing is a little leaky
 			String complete_path = concatenate_strings(heap_allocator(), output_base, output_ext);
 			complete_path = path_to_full_path(heap_allocator(), complete_path);
-			system_exec_command_line_app("odin run", false, "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
+			system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
 		}
 	#endif
 #endif

+ 5 - 3
src/tokenizer.cpp

@@ -212,6 +212,8 @@ void warning_va(Token token, char *fmt, va_list va) {
 	gb_mutex_unlock(&global_error_collector.mutex);
 }
 
+#define MAX_ERROR_COLLECTOR_COUNT (36)
+
 void error_va(Token token, char *fmt, va_list va) {
 	gb_mutex_lock(&global_error_collector.mutex);
 	global_error_collector.count++;
@@ -225,7 +227,7 @@ void error_va(Token token, char *fmt, va_list va) {
 		              gb_bprintf_va(fmt, va));
 	}
 	gb_mutex_unlock(&global_error_collector.mutex);
-	if (global_error_collector.count > 20) {
+	if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
 		gb_exit(1);
 	}
 }
@@ -243,7 +245,7 @@ void error_no_newline_va(Token token, char *fmt, va_list va) {
 		              gb_bprintf_va(fmt, va));
 	}
 	gb_mutex_unlock(&global_error_collector.mutex);
-	if (global_error_collector.count > 20) {
+	if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
 		gb_exit(1);
 	}
 }
@@ -263,7 +265,7 @@ void syntax_error_va(Token token, char *fmt, va_list va) {
 	}
 
 	gb_mutex_unlock(&global_error_collector.mutex);
-	if (global_error_collector.count > 20) {
+	if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
 		gb_exit(1);
 	}
 }