Переглянути джерело

#rune "" to ''; Remove infix and postfix call notation

Ginger Bill 8 роки тому
батько
коміт
598dab5bc4
19 змінених файлів з 371 додано та 324 видалено
  1. 1 1
      build.bat
  2. 30 1
      code/demo.odin
  3. 1 1
      code/game.odin
  4. 13 13
      core/fmt.odin
  5. 2 2
      core/utf8.odin
  6. 39 37
      core/win32.odin
  7. 3 3
      src/array.c
  8. 13 5
      src/checker/checker.c
  9. 14 18
      src/checker/expr.c
  10. 2 1
      src/checker/types.c
  11. 13 7
      src/common.c
  12. 18 18
      src/exact_value.c
  13. 6 3
      src/map.c
  14. 58 98
      src/parser.c
  15. 0 19
      src/ssa.c
  16. 107 80
      src/string.c
  17. 4 3
      src/timings.c
  18. 46 13
      src/tokenizer.c
  19. 1 1
      src/unicode.c

+ 1 - 1
build.bat

@@ -16,7 +16,7 @@ if %release_mode% EQU 0 ( rem Debug
 )
 
 set compiler_warnings= ^
-	-we4013 -we4706 -we4002 ^
+	-we4013 -we4706 -we4002 -we4133 ^
 	-wd4100 -wd4101 -wd4127 -wd4189 ^
 	-wd4201 -wd4204 -wd4244 ^
 	-wd4306 ^

+ 30 - 1
code/demo.odin

@@ -1,5 +1,34 @@
 #import "fmt.odin"
+#import "utf8.odin"
 
 main :: proc() {
-	fmt.println("Hellope, World!")
+	MAX :: 64
+	buf:     [MAX]rune
+	backing: [MAX]byte
+	offset:  int
+
+	msg := "Hello"
+	count := utf8.rune_count(msg)
+	assert(count <= MAX)
+	runes := buf[:count]
+
+	offset = 0
+	for i := 0; i < count; i++ {
+		s := msg[offset:]
+		r, len := utf8.decode_rune(s)
+		runes[count-i-1] = r
+		offset += len
+	}
+
+	offset = 0
+	for i := 0; i < count; i++ {
+		data, len := utf8.encode_rune(runes[i])
+		for j := 0; j < len; j++ {
+			backing[offset+j] = data[j]
+		}
+		offset += len
+	}
+
+	reverse := backing[:count] as string
+	fmt.println(reverse)
 }

+ 1 - 1
code/game.odin

@@ -4,7 +4,7 @@
 #import "os.odin"
 #import "opengl.odin" as gl
 
-TWO_HEARTS :: #rune "💕"
+TWO_HEARTS :: '💕'
 
 win32_perf_count_freq := win32.GetQueryPerformanceFrequency()
 time_now :: proc() -> f64 {

+ 13 - 13
core/fmt.odin

@@ -76,8 +76,8 @@ print_rune_to_buffer :: proc(buf: ^[]byte, r: rune) {
 	print_string_to_buffer(buf, b[:n] as string)
 }
 
-print_space_to_buffer :: proc(buf: ^[]byte) { print_rune_to_buffer(buf, #rune " ") }
-print_nl_to_buffer    :: proc(buf: ^[]byte) { print_rune_to_buffer(buf, #rune "\n") }
+print_space_to_buffer :: proc(buf: ^[]byte) { print_rune_to_buffer(buf, ' ') }
+print_nl_to_buffer    :: proc(buf: ^[]byte) { print_rune_to_buffer(buf, '\n') }
 
 __NUM_TO_CHAR_TABLE := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@$"
 
@@ -99,7 +99,7 @@ print_u64_to_buffer :: proc(buffer: ^[]byte, value: u64) {
 	buf: [20]byte
 	len := 0
 	if i == 0 {
-		buf[len] = #rune "0"
+		buf[len] = '0'
 		len++
 	}
 	for i > 0 {
@@ -115,7 +115,7 @@ print_i64_to_buffer :: proc(buffer: ^[]byte, value: i64) {
 	neg := i < 0
 	if neg {
 		i = -i
-		print_rune_to_buffer(buffer, #rune "-")
+		print_rune_to_buffer(buffer, '-')
 	}
 	print_u64_to_buffer(buffer, i as u64)
 }
@@ -132,7 +132,7 @@ print_i128_to_buffer :: proc(buffer: ^[]byte, value: i128) {
 	neg := i < 0
 	if neg {
 		i = -i
-		print_rune_to_buffer(buffer, #rune "-")
+		print_rune_to_buffer(buffer, '-')
 	}
 	print_u128_to_buffer(buffer, i as u128)
 }
@@ -142,11 +142,11 @@ print_i128_to_buffer :: proc(buffer: ^[]byte, value: i128) {
 print__f64 :: proc(buffer: ^[]byte, value: f64, decimal_places: int) {
 	f := value
 	if f == 0 {
-		print_rune_to_buffer(buffer, #rune "0")
+		print_rune_to_buffer(buffer, '0')
 		return
 	}
 	if f < 0 {
-		print_rune_to_buffer(buffer, #rune "-")
+		print_rune_to_buffer(buffer, '-')
 		f = -f
 	}
 
@@ -154,7 +154,7 @@ print__f64 :: proc(buffer: ^[]byte, value: f64, decimal_places: int) {
 	print_u64_to_buffer(buffer, i)
 	f -= i as f64
 
-	print_rune_to_buffer(buffer, #rune ".")
+	print_rune_to_buffer(buffer, '.')
 
 	mult: f64 = 10.0
 	for ; decimal_places >= 0; decimal_places-- {
@@ -488,7 +488,7 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any) {
 
 bprintf :: proc(buf: ^[]byte, fmt: string, args: ..any) -> int {
 	is_digit :: proc(r: rune) -> bool #inline {
-		return r >= #rune "0" && r <= #rune "9"
+		return '0' <= r && r <= '9'
 	}
 
 	parse_int :: proc(s: string, offset: int) -> (int, int) {
@@ -501,7 +501,7 @@ bprintf :: proc(buf: ^[]byte, fmt: string, args: ..any) -> int {
 			}
 
 			result *= 10
-			result += (c - #rune "0") as int
+			result += (c - '0') as int
 		}
 
 		return result, offset
@@ -514,7 +514,7 @@ bprintf :: proc(buf: ^[]byte, fmt: string, args: ..any) -> int {
 		r := fmt[i] as rune
 		index := implicit_index
 
-		if r != #rune "%" {
+		if r != '%' {
 			continue
 		}
 
@@ -523,7 +523,7 @@ bprintf :: proc(buf: ^[]byte, fmt: string, args: ..any) -> int {
 		if i < fmt.count {
 			next := fmt[i] as rune
 
-			if next == #rune "%" {
+			if next == '%' {
 				print_string_to_buffer(buf, "%")
 				i++
 				prev = i
@@ -582,7 +582,7 @@ bprint :: proc(buf: ^[]byte, args: ..any) -> int {
 bprintln :: proc(buf: ^[]byte, args: ..any) -> int {
 	for i := 0; i < args.count; i++ {
 		if i > 0 {
-			append(buf, #rune " ")
+			append(buf, ' ')
 		}
 		print_any_to_buffer(buf, args[i])
 	}

+ 2 - 2
core/utf8.odin

@@ -1,8 +1,8 @@
-RUNE_ERROR :: #rune "\ufffd"
+RUNE_ERROR :: '\ufffd'
 RUNE_SELF  :: 0x80
 RUNE_BOM   :: 0xfeff
 RUNE_EOF   :: ~(0 as rune)
-MAX_RUNE   :: #rune "\U0010ffff"
+MAX_RUNE   :: '\U0010ffff'
 UTF_MAX    :: 4
 
 

+ 39 - 37
core/win32.odin

@@ -1,6 +1,8 @@
 #foreign_system_library "user32"
 #foreign_system_library "gdi32"
 
+_:= compile_assert(ODIN_OS == "windows")
+
 HANDLE    :: type rawptr
 HWND      :: type HANDLE
 HDC       :: type HANDLE
@@ -387,43 +389,43 @@ Key_Code :: enum i32 {
 	DELETE     = 0x2E,
 	HELP       = 0x2F,
 
-	NUM0 = #rune "0",
-	NUM1 = #rune "1",
-	NUM2 = #rune "2",
-	NUM3 = #rune "3",
-	NUM4 = #rune "4",
-	NUM5 = #rune "5",
-	NUM6 = #rune "6",
-	NUM7 = #rune "7",
-	NUM8 = #rune "8",
-	NUM9 = #rune "9",
-
-	A = #rune "A",
-	B = #rune "B",
-	C = #rune "C",
-	D = #rune "D",
-	E = #rune "E",
-	F = #rune "F",
-	G = #rune "G",
-	H = #rune "H",
-	I = #rune "I",
-	J = #rune "J",
-	K = #rune "K",
-	L = #rune "L",
-	M = #rune "M",
-	N = #rune "N",
-	O = #rune "O",
-	P = #rune "P",
-	Q = #rune "Q",
-	R = #rune "R",
-	S = #rune "S",
-	T = #rune "T",
-	U = #rune "U",
-	V = #rune "V",
-	W = #rune "W",
-	X = #rune "X",
-	Y = #rune "Y",
-	Z = #rune "Z",
+	NUM0 = '0',
+	NUM1 = '1',
+	NUM2 = '2',
+	NUM3 = '3',
+	NUM4 = '4',
+	NUM5 = '5',
+	NUM6 = '6',
+	NUM7 = '7',
+	NUM8 = '8',
+	NUM9 = '9',
+
+	A = 'A',
+	B = 'B',
+	C = 'C',
+	D = 'D',
+	E = 'E',
+	F = 'F',
+	G = 'G',
+	H = 'H',
+	I = 'I',
+	J = 'J',
+	K = 'K',
+	L = 'L',
+	M = 'M',
+	N = 'N',
+	O = 'O',
+	P = 'P',
+	Q = 'Q',
+	R = 'R',
+	S = 'S',
+	T = 'T',
+	U = 'U',
+	V = 'V',
+	W = 'W',
+	X = 'X',
+	Y = 'Y',
+	Z = 'Z',
 
 	LWIN       = 0x5B,
 	RWIN       = 0x5C,

+ 3 - 3
src/array.c

@@ -11,8 +11,8 @@ GB_STATIC_ASSERT(ARRAY_GROW_FORMULA(0) > 0);
 typedef Array(void) ArrayVoid;
 
 #define array_init_reserve(x_, allocator_, init_capacity_) do { \
-	GB_ASSERT((x_) != NULL); \
 	void **e = cast(void **)&((x_)->e); \
+	GB_ASSERT((x_) != NULL); \
 	(x_)->allocator = (allocator_); \
 	(x_)->count = 0; \
 	(x_)->capacity = (init_capacity_); \
@@ -20,8 +20,8 @@ typedef Array(void) ArrayVoid;
 } while (0)
 
 #define array_init_count(x_, allocator_, init_count_) do { \
-	GB_ASSERT((x_) != NULL); \
 	void **e = cast(void **)&((x_)->e); \
+	GB_ASSERT((x_) != NULL); \
 	(x_)->allocator = (allocator_); \
 	(x_)->count = (init_count_); \
 	(x_)->capacity = (init_count_); \
@@ -67,8 +67,8 @@ typedef Array(void) ArrayVoid;
 
 
 void array__set_capacity(void *ptr, isize capacity, isize element_size) {
-	GB_ASSERT(ptr != NULL);
 	ArrayVoid *x = cast(ArrayVoid *)ptr;
+	GB_ASSERT(ptr != NULL);
 
 	GB_ASSERT(element_size > 0);
 

+ 13 - 5
src/checker/checker.c

@@ -271,8 +271,9 @@ CycleChecker *cycle_checker_add(CycleChecker *cc, Entity *e) {
 	if (cc->path.e == NULL) {
 		array_init(&cc->path, heap_allocator());
 	}
-	GB_ASSERT(e != NULL && e->kind == Entity_TypeName);
-	array_add(&cc->path, e);
+	if (e != NULL && e->kind == Entity_TypeName) {
+		array_add(&cc->path, e);
+	}
 	return cc;
 }
 
@@ -508,6 +509,11 @@ void add_global_constant(gbAllocator a, String name, Type *type, ExactValue valu
 }
 
 
+void add_global_string_constant(gbAllocator a, String name, String value) {
+	add_global_constant(a, name, t_untyped_string, make_exact_value_string(value));
+
+}
+
 
 void init_universal_scope(void) {
 	// NOTE(bill): No need to free these
@@ -528,9 +534,11 @@ void init_universal_scope(void) {
 
 	add_global_entity(make_entity_nil(a, str_lit("nil"), t_untyped_nil));
 
-	add_global_constant(a, str_lit("ODIN_OS"),      t_untyped_string, make_exact_value_string(str_lit("windows")));
-	add_global_constant(a, str_lit("ODIN_ARCH"),    t_untyped_string, make_exact_value_string(str_lit("amd64")));
-	add_global_constant(a, str_lit("ODIN_VERSION"), t_untyped_string, make_exact_value_string(str_lit(VERSION_STRING)));
+	add_global_string_constant(a, str_lit("ODIN_OS"),      str_lit("windows"));
+	add_global_string_constant(a, str_lit("ODIN_ARCH"),    str_lit("amd64"));
+	add_global_string_constant(a, str_lit("ODIN_VENDOR"),  str_lit("odin"));
+	add_global_string_constant(a, str_lit("ODIN_VERSION"), str_lit(VERSION_STRING));
+	add_global_string_constant(a, str_lit("ODIN_ENDIAN"),  str_lit("little"));
 
 
 // Builtin Procedures

+ 14 - 18
src/checker/expr.c

@@ -713,12 +713,10 @@ void check_enum_type(Checker *c, Type *enum_type, Type *named_type, AstNode *nod
 			if (o.mode != Addressing_Invalid) {
 				iota = o.value;
 			} else {
-				Token add_token = {Token_Add};
-				iota = exact_binary_operator_value(add_token, iota, make_exact_value_integer(1));
+				iota = exact_binary_operator_value(Token_Add, iota, make_exact_value_integer(1));
 			}
 		} else {
-			Token add_token = {Token_Add};
-			iota = exact_binary_operator_value(add_token, iota, make_exact_value_integer(1));
+			iota = exact_binary_operator_value(Token_Add, iota, make_exact_value_integer(1));
 		}
 
 
@@ -1492,7 +1490,7 @@ void check_unary_expr(Checker *c, Operand *o, Token op, AstNode *node) {
 		if (is_type_unsigned(type)) {
 			precision = cast(i32)(8 * type_size_of(c->sizes, c->allocator, type));
 		}
-		o->value = exact_unary_operator_value(op, o->value, precision);
+		o->value = exact_unary_operator_value(op.kind, o->value, precision);
 
 		if (is_type_typed(type)) {
 			if (node != NULL) {
@@ -1558,7 +1556,7 @@ void check_comparison(Checker *c, Operand *x, Operand *y, Token op) {
 	} else {
 		if (x->mode == Addressing_Constant &&
 		    y->mode == Addressing_Constant) {
-			x->value = make_exact_value_bool(compare_exact_values(op, x->value, y->value));
+			x->value = make_exact_value_bool(compare_exact_values(op.kind, x->value, y->value));
 		} else {
 			x->mode = Addressing_Value;
 
@@ -1645,7 +1643,7 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
 				x->type = t_untyped_integer;
 			}
 
-			x->value = exact_value_shift(be->op, x_val, make_exact_value_integer(amount));
+			x->value = exact_value_shift(be->op.kind, x_val, make_exact_value_integer(amount));
 
 			if (is_type_typed(x->type)) {
 				check_is_expressible(c, x, base_type(x->type));
@@ -2168,7 +2166,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 		if (op.kind == Token_Quo && is_type_integer(type)) {
 			op.kind = Token_QuoEq; // NOTE(bill): Hack to get division of integers
 		}
-		x->value = exact_binary_operator_value(op, a, b);
+		x->value = exact_binary_operator_value(op.kind, a, b);
 		if (is_type_typed(type)) {
 			if (node != NULL) {
 				x->expr = node;
@@ -3166,7 +3164,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 	case BuiltinProc_min: {
 		// min :: proc(a, b: comparable) -> comparable
 		Type *type = base_type(operand->type);
-		if (!is_type_comparable(type) || !is_type_numeric(type)) {
+		if (!is_type_comparable(type) || !(is_type_numeric(type) || is_type_string(type))) {
 			gbString type_str = type_to_string(operand->type);
 			error(ast_node_token(call),
 			      "Expected a comparable numeric type to `min`, got `%s`",
@@ -3182,7 +3180,7 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		if (b.mode == Addressing_Invalid) {
 			return false;
 		}
-		if (!is_type_comparable(b.type) || !is_type_numeric(type)) {
+		if (!is_type_comparable(b.type) || !(is_type_numeric(b.type) || is_type_string(b.type))) {
 			gbString type_str = type_to_string(b.type);
 			error(ast_node_token(call),
 			      "Expected a comparable numeric type to `min`, got `%s`",
@@ -3195,10 +3193,9 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		    b.mode == Addressing_Constant) {
 			ExactValue x = a.value;
 			ExactValue y = b.value;
-			Token lt = {Token_Lt};
 
 			operand->mode = Addressing_Constant;
-			if (compare_exact_values(lt, x, y)) {
+			if (compare_exact_values(Token_Lt, x, y)) {
 				operand->value = x;
 				operand->type = a.type;
 			} else {
@@ -3235,10 +3232,10 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 	case BuiltinProc_max: {
 		// min :: proc(a, b: comparable) -> comparable
 		Type *type = base_type(operand->type);
-		if (!is_type_comparable(type) || !is_type_numeric(type)) {
+		if (!is_type_comparable(type) || !(is_type_numeric(type) || is_type_string(type))) {
 			gbString type_str = type_to_string(operand->type);
 			error(ast_node_token(call),
-			      "Expected a comparable numeric type to `max`, got `%s`",
+			      "Expected a comparable numeric or string type to `max`, got `%s`",
 			      type_str);
 			gb_string_free(type_str);
 			return false;
@@ -3251,10 +3248,10 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		if (b.mode == Addressing_Invalid) {
 			return false;
 		}
-		if (!is_type_comparable(b.type) || !is_type_numeric(type)) {
+		if (!is_type_comparable(b.type) || !(is_type_numeric(type) || is_type_string(type))) {
 			gbString type_str = type_to_string(b.type);
 			error(ast_node_token(call),
-			      "Expected a comparable numeric type to `max`, got `%s`",
+			      "Expected a comparable numeric or string type to `max`, got `%s`",
 			      type_str);
 			gb_string_free(type_str);
 			return false;
@@ -3264,10 +3261,9 @@ bool check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id
 		    b.mode == Addressing_Constant) {
 			ExactValue x = a.value;
 			ExactValue y = b.value;
-			Token gt = {Token_Gt};
 
 			operand->mode = Addressing_Constant;
-			if (compare_exact_values(gt, x, y)) {
+			if (compare_exact_values(Token_Gt, x, y)) {
 				operand->value = x;
 				operand->type = a.type;
 			} else {

+ 2 - 1
src/checker/types.c

@@ -683,7 +683,8 @@ bool is_type_comparable(Type *t) {
 		return false;
 	} break;
 	case Type_Array:
-		return is_type_comparable(t->Array.elem);
+		return false;
+		// return is_type_comparable(t->Array.elem);
 	case Type_Vector:
 		return is_type_comparable(t->Vector.elem);
 	case Type_Proc:

+ 13 - 7
src/common.c

@@ -14,14 +14,19 @@ gb_global bool global_module_path_set = false;
 
 
 String get_module_dir() {
+	String path = global_module_path;
+	Array(wchar_t) path_buf;
+	isize len, i;
+	gbTempArenaMemory tmp;
+	wchar_t *text;
+
 	if (global_module_path_set) {
 		return global_module_path;
 	}
 
-	Array(wchar_t) path_buf;
 	array_init_count(&path_buf, heap_allocator(), 300);
 
-	isize len = 0;
+	len = 0;
 	for (;;) {
 		len = GetModuleFileNameW(NULL, &path_buf.e[0], path_buf.count);
 		if (len == 0) {
@@ -33,13 +38,13 @@ String get_module_dir() {
 		array_resize(&path_buf, 2*path_buf.count + 300);
 	}
 
-	gbTempArenaMemory tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
+	tmp = gb_temp_arena_memory_begin(&string_buffer_arena);
 
-	wchar_t *text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
+	text = gb_alloc_array(string_buffer_allocator, wchar_t, len+1);
 
 	GetModuleFileNameW(NULL, text, len);
-	String path = string16_to_string(heap_allocator(), make_string16(text, len));
-	for (isize i = path.len-1; i >= 0; i--) {
+	path = string16_to_string(heap_allocator(), make_string16(text, len));
+	for (i = path.len-1; i >= 0; i--) {
 		u8 c = path.text[i];
 		if (c == '/' || c == '\\') {
 			break;
@@ -141,8 +146,9 @@ i16 f32_to_f16(f32 value) {
 		if (e > 30) {
 			float volatile f = 1e12f;
 			int j;
-			for (j = 0; j < 10; j++)
+			for (j = 0; j < 10; j++) {
 				f *= f; /* NOTE(bill): Cause overflow */
+			}
 
 			return cast(i16)(s | 0x7c00);
 		}

+ 18 - 18
src/exact_value.c

@@ -148,8 +148,8 @@ ExactValue exact_value_to_float(ExactValue v) {
 }
 
 
-ExactValue exact_unary_operator_value(Token op, ExactValue v, i32 precision) {
-	switch (op.kind) {
+ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) {
+	switch (op) {
 	case Token_Add:	{
 		switch (v.kind) {
 		case ExactValue_Invalid:
@@ -207,7 +207,7 @@ ExactValue exact_unary_operator_value(Token op, ExactValue v, i32 precision) {
 	}
 
 failure:
-	GB_PANIC("Invalid unary operation, %.*s", LIT(token_strings[op.kind]));
+	GB_PANIC("Invalid unary operation, %.*s", LIT(token_strings[op]));
 
 	ExactValue error_value = {0};
 	return error_value;
@@ -270,7 +270,7 @@ void match_exact_values(ExactValue *x, ExactValue *y) {
 }
 
 // TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
-ExactValue exact_binary_operator_value(Token op, ExactValue x, ExactValue y) {
+ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) {
 	match_exact_values(&x, &y);
 
 	switch (x.kind) {
@@ -278,7 +278,7 @@ ExactValue exact_binary_operator_value(Token op, ExactValue x, ExactValue y) {
 		return x;
 
 	case ExactValue_Bool:
-		switch (op.kind) {
+		switch (op) {
 		case Token_CmpAnd: return make_exact_value_bool(x.value_bool && y.value_bool);
 		case Token_CmpOr:  return make_exact_value_bool(x.value_bool || y.value_bool);
 		case Token_And:    return make_exact_value_bool(x.value_bool & y.value_bool);
@@ -291,7 +291,7 @@ ExactValue exact_binary_operator_value(Token op, ExactValue x, ExactValue y) {
 		i64 a = x.value_integer;
 		i64 b = y.value_integer;
 		i64 c = 0;
-		switch (op.kind) {
+		switch (op) {
 		case Token_Add:    c = a + b;  break;
 		case Token_Sub:    c = a - b;  break;
 		case Token_Mul:    c = a * b;  break;
@@ -312,7 +312,7 @@ ExactValue exact_binary_operator_value(Token op, ExactValue x, ExactValue y) {
 	case ExactValue_Float: {
 		f64 a = x.value_float;
 		f64 b = y.value_float;
-		switch (op.kind) {
+		switch (op) {
 		case Token_Add: return make_exact_value_float(a + b);
 		case Token_Sub: return make_exact_value_float(a - b);
 		case Token_Mul: return make_exact_value_float(a * b);
@@ -324,22 +324,22 @@ ExactValue exact_binary_operator_value(Token op, ExactValue x, ExactValue y) {
 
 error:
 	ExactValue error_value = {0};
-	// gb_printf_err("Invalid binary operation: %s\n", token_kind_to_string(op.kind));
+	// gb_printf_err("Invalid binary operation: %s\n", token_kind_to_string(op));
 	return error_value;
 }
 
-gb_inline ExactValue exact_value_add(ExactValue x, ExactValue y) { Token op = {Token_Add};        return exact_binary_operator_value(op, x, y); }
-gb_inline ExactValue exact_value_sub(ExactValue x, ExactValue y) { Token op = {Token_Sub};        return exact_binary_operator_value(op, x, y); }
-gb_inline ExactValue exact_value_mul(ExactValue x, ExactValue y) { Token op = {Token_Mul};        return exact_binary_operator_value(op, x, y); }
-gb_inline ExactValue exact_value_quo(ExactValue x, ExactValue y) { Token op = {Token_Quo};        return exact_binary_operator_value(op, x, y); }
-gb_inline ExactValue exact_value_shift(Token op, ExactValue x, ExactValue y) { return exact_binary_operator_value(op, x, y); }
+gb_inline ExactValue exact_value_add(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Add, x, y); }
+gb_inline ExactValue exact_value_sub(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Sub, x, y); }
+gb_inline ExactValue exact_value_mul(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Mul, x, y); }
+gb_inline ExactValue exact_value_quo(ExactValue x, ExactValue y) { return exact_binary_operator_value(Token_Quo, x, y); }
+gb_inline ExactValue exact_value_shift(TokenKind op, ExactValue x, ExactValue y) { return exact_binary_operator_value(op, x, y); }
 
 
 i32 cmp_f64(f64 a, f64 b) {
 	return (a > b) - (a < b);
 }
 
-bool compare_exact_values(Token op, ExactValue x, ExactValue y) {
+bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y) {
 	match_exact_values(&x, &y);
 
 	switch (x.kind) {
@@ -347,7 +347,7 @@ bool compare_exact_values(Token op, ExactValue x, ExactValue y) {
 		return false;
 
 	case ExactValue_Bool:
-		switch (op.kind) {
+		switch (op) {
 		case Token_CmpEq: return x.value_bool == y.value_bool;
 		case Token_NotEq: return x.value_bool != y.value_bool;
 		}
@@ -356,7 +356,7 @@ bool compare_exact_values(Token op, ExactValue x, ExactValue y) {
 	case ExactValue_Integer: {
 		i64 a = x.value_integer;
 		i64 b = y.value_integer;
-		switch (op.kind) {
+		switch (op) {
 		case Token_CmpEq: return a == b;
 		case Token_NotEq: return a != b;
 		case Token_Lt:    return a <  b;
@@ -369,7 +369,7 @@ bool compare_exact_values(Token op, ExactValue x, ExactValue y) {
 	case ExactValue_Float: {
 		f64 a = x.value_float;
 		f64 b = y.value_float;
-		switch (op.kind) {
+		switch (op) {
 		case Token_CmpEq: return cmp_f64(a, b) == 0;
 		case Token_NotEq: return cmp_f64(a, b) != 0;
 		case Token_Lt:    return cmp_f64(a, b) <  0;
@@ -384,7 +384,7 @@ bool compare_exact_values(Token op, ExactValue x, ExactValue y) {
 		String b = y.value_string;
 		isize len = gb_min(a.len, b.len);
 		// TODO(bill): gb_memcompare is used because the strings are UTF-8
-		switch (op.kind) {
+		switch (op) {
 		case Token_CmpEq: return gb_memcompare(a.text, b.text, len) == 0;
 		case Token_NotEq: return gb_memcompare(a.text, b.text, len) != 0;
 		case Token_Lt:    return gb_memcompare(a.text, b.text, len) <  0;

+ 6 - 3
src/map.c

@@ -243,6 +243,7 @@ void _J2(MAP_PROC,set)(MAP_NAME *h, HashKey key, MAP_TYPE value) {
 
 
 void _J2(MAP_PROC,_erase)(MAP_NAME *h, MapFindResult fr) {
+	MapFindResult last;
 	if (fr.entry_prev < 0) {
 		h->hashes.e[fr.hash_index] = h->entries.e[fr.entry_index].next;
 	} else {
@@ -253,7 +254,7 @@ void _J2(MAP_PROC,_erase)(MAP_NAME *h, MapFindResult fr) {
 		return;
 	}
 	h->entries.e[fr.entry_index] = h->entries.e[h->entries.count-1];
-	MapFindResult last = _J2(MAP_PROC,_find)(h, h->entries.e[fr.entry_index].key);
+	last = _J2(MAP_PROC,_find)(h, h->entries.e[fr.entry_index].key);
 	if (last.entry_prev >= 0) {
 		h->entries.e[last.entry_prev].next = fr.entry_index;
 	} else {
@@ -314,11 +315,13 @@ void _J2(MAP_PROC,multi_get_all)(MAP_NAME *h, HashKey key, MAP_TYPE *items) {
 }
 
 void _J2(MAP_PROC,multi_insert)(MAP_NAME *h, HashKey key, MAP_TYPE value) {
+	MapFindResult fr;
+	isize i;
 	if (h->hashes.count == 0) {
 		_J2(MAP_PROC,grow)(h);
 	}
-	MapFindResult fr = _J2(MAP_PROC,_find)(h, key);
-	isize i = _J2(MAP_PROC,_add_entry)(h, key);
+	fr = _J2(MAP_PROC,_find)(h, key);
+	i = _J2(MAP_PROC,_add_entry)(h, key);
 	if (fr.entry_prev < 0) {
 		h->hashes.e[fr.hash_index] = i;
 	} else {

+ 58 - 98
src/parser.c

@@ -32,7 +32,7 @@ typedef struct AstFile {
 	isize          expr_level;
 
 	AstNodeArray   decls;
-	bool            is_global_scope;
+	bool           is_global_scope;
 
 	AstNode *      curr_proc;
 	isize          scope_level;
@@ -86,13 +86,6 @@ typedef enum StmtStateFlag {
 	StmtStateFlag_no_bounds_check = GB_BIT(1),
 } StmtStateFlag;
 
-
-typedef enum CallExprKind {
-	CallExpr_Prefix,  // call(...)
-	CallExpr_Postfix, // a'call
-	CallExpr_Infix,   // a ''call b
-} CallExprKind;
-
 AstNodeArray make_ast_node_array(AstFile *f) {
 	AstNodeArray a;
 	array_init(&a, gb_arena_allocator(&f->arena));
@@ -134,7 +127,6 @@ AST_NODE_KIND(_ExprBegin,  "",  i32) \
 		AstNodeArray args; \
 		Token open, close; \
 		Token ellipsis; \
-		CallExprKind kind; \
 	}) \
 	AST_NODE_KIND(SliceExpr, "slice expression", struct { \
 		AstNode *expr; \
@@ -256,9 +248,10 @@ AST_NODE_KIND(_DeclBegin,      "", i32) \
 	}) \
 	AST_NODE_KIND(ImportDecl, "import declaration", struct { \
 		Token token, relpath; \
+		String os, arch;      \
 		String fullpath;      \
 		Token import_name;    \
-		bool is_load;          \
+		bool is_load;         \
 		AstNode *note;        \
 	}) \
 	AST_NODE_KIND(ForeignLibrary, "foreign library", struct { \
@@ -959,11 +952,15 @@ AstNode *make_type_decl(AstFile *f, Token token, AstNode *name, AstNode *type) {
 	return result;
 }
 
-AstNode *make_import_decl(AstFile *f, Token token, Token relpath, Token import_name, bool is_load) {
+AstNode *make_import_decl(AstFile *f, Token token, Token relpath, Token import_name,
+                          String os, String arch,
+                          bool is_load) {
 	AstNode *result = make_node(f, AstNode_ImportDecl);
 	result->ImportDecl.token = token;
 	result->ImportDecl.relpath = relpath;
 	result->ImportDecl.import_name = import_name;
+	result->ImportDecl.os = os;
+	result->ImportDecl.arch = arch;
 	result->ImportDecl.is_load = is_load;
 	return result;
 }
@@ -1374,19 +1371,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) {
 	case Token_Hash: {
 		Token token = expect_token(f, Token_Hash);
 		Token name  = expect_token(f, Token_Identifier);
-		if (str_eq(name.string, str_lit("rune"))) {
-			if (f->curr_token.kind == Token_String) {
-				Token *s = &f->curr_token;
-
-				if (gb_utf8_strnlen(s->string.text, s->string.len) != 1) {
-					syntax_error(*s, "Invalid rune literal %.*s", LIT(s->string));
-				}
-				s->kind = Token_Rune; // NOTE(bill): Change it
-			} else {
-				expect_token(f, Token_String);
-			}
-			operand = parse_operand(f, lhs);
-		} else if (str_eq(name.string, str_lit("file"))) {
+		if (str_eq(name.string, str_lit("file"))) {
 			Token token = name;
 			token.kind = Token_String;
 			token.string = token.pos.file;
@@ -1513,19 +1498,6 @@ AstNode *parse_atom_expr(AstFile *f, bool lhs) {
 	bool loop = true;
 	while (loop) {
 		switch (f->curr_token.kind) {
-
-		case Token_Prime: {
-			Token op = expect_token(f, Token_Prime);
-			if (lhs) {
-				// TODO(bill): Handle this
-			}
-			AstNode *proc = parse_identifier(f);
-			AstNodeArray args;
-			array_init_reserve(&args, gb_arena_allocator(&f->arena), 1);
-			array_add(&args, operand);
-			operand = make_call_expr(f, proc, args, ast_node_token(operand), op, empty_token);
-		} break;
-
 		case Token_OpenParen: {
 			if (lhs) {
 				// TODO(bill): Handle this shit! Is this even allowed in this language?!
@@ -1680,13 +1652,11 @@ i32 token_precedence(Token t) {
 	case Token_Shl:
 	case Token_Shr:
 		return 5;
-	case Token_DoublePrime:
-		return 6;
 	case Token_as:
 	case Token_transmute:
 	case Token_down_cast:
 	case Token_union_cast:
-		return 7;
+		return 6;
 	}
 
 	return 0;
@@ -1708,28 +1678,6 @@ AstNode *parse_binary_expr(AstFile *f, bool lhs, i32 prec_in) {
 			}
 
 			switch (op.kind) {
-			case Token_DoublePrime: {
-				// TODO(bill): Properly define semantic for in-fix and post-fix calls
-				AstNode *proc = parse_identifier(f);
-				/* if (f->curr_token.kind == Token_OpenParen) {
-					AstNode *call = parse_call_expr(f, proc);
-					array_add(&call->CallExpr.args, expression);
-					for (isize i = gb_array_count(call->CallExpr.args)-1; i > 0; i--) {
-						gb_swap(AstNode *, call->CallExpr.args[i], call->CallExpr.args[i-1]);
-					}
-
-					expression = call;
-				} else  */{
-					right = parse_binary_expr(f, false, prec+1);
-					AstNodeArray args = {0};
-					array_init_reserve(&args, gb_arena_allocator(&f->arena), 2);
-					array_add(&args, expression);
-					array_add(&args, right);
-					expression = make_call_expr(f, proc, args, op, ast_node_token(right), empty_token);
-				}
-				continue;
-			} break;
-
 			case Token_as:
 			case Token_transmute:
 			case Token_down_cast:
@@ -2755,42 +2703,6 @@ AstNode *parse_stmt(AstFile *f) {
 			}
 			syntax_error(token, "You cannot use #shared_global_scope within a procedure. This must be done at the file scope");
 			return make_bad_decl(f, token, f->curr_token);
-		} else if (str_eq(tag, str_lit("import"))) {
-			// TODO(bill): better error messages
-			Token import_name = {0};
-			Token file_path = expect_token_after(f, Token_String, "#import");
-			if (allow_token(f, Token_as)) {
-				// NOTE(bill): Custom import name
-				if (f->curr_token.kind == Token_Period) {
-					import_name = f->curr_token;
-					import_name.kind = Token_Identifier;
-					next_token(f);
-				} else {
-					import_name = expect_token_after(f, Token_Identifier, "`as` for import declaration");
-				}
-
-				if (str_eq(import_name.string, str_lit("_"))) {
-					syntax_error(token, "Illegal import name: `_`");
-					return make_bad_decl(f, token, f->curr_token);
-				}
-			}
-
-			if (f->curr_proc == NULL) {
-				return make_import_decl(f, s->TagStmt.token, file_path, import_name, false);
-			}
-			syntax_error(token, "You cannot use #import within a procedure. This must be done at the file scope");
-			return make_bad_decl(f, token, file_path);
-		} else if (str_eq(tag, str_lit("load"))) {
-			// TODO(bill): better error messages
-			Token file_path = expect_token(f, Token_String);
-			Token import_name = file_path;
-			import_name.string = str_lit(".");
-
-			if (f->curr_proc == NULL) {
-				return make_import_decl(f, s->TagStmt.token, file_path, import_name, true);
-			}
-			syntax_error(token, "You cannot use #load within a procedure. This must be done at the file scope");
-			return make_bad_decl(f, token, file_path);
 		} else if (str_eq(tag, str_lit("foreign_system_library"))) {
 			Token file_path = expect_token(f, Token_String);
 			if (f->curr_proc == NULL) {
@@ -2831,6 +2743,54 @@ AstNode *parse_stmt(AstFile *f) {
 				syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together");
 			}
 			return s;
+		} else if (str_eq(tag, str_lit("import"))) {
+			String os = {0};
+			String arch = {0};
+
+			// if (tag.len > 6) {
+			// 	String sub = make_string(tag.text+6, tag.len-6);
+			// }
+
+			// TODO(bill): better error messages
+			Token import_name = {0};
+			Token file_path = expect_token_after(f, Token_String, "#import");
+			if (allow_token(f, Token_as)) {
+				// NOTE(bill): Custom import name
+				if (f->curr_token.kind == Token_Period) {
+					import_name = f->curr_token;
+					import_name.kind = Token_Identifier;
+					next_token(f);
+				} else {
+					import_name = expect_token_after(f, Token_Identifier, "`as` for import declaration");
+				}
+
+				if (str_eq(import_name.string, str_lit("_"))) {
+					syntax_error(token, "Illegal import name: `_`");
+					return make_bad_decl(f, token, f->curr_token);
+				}
+			}
+
+			if (f->curr_proc != NULL) {
+				syntax_error(token, "You cannot use #import within a procedure. This must be done at the file scope");
+				return make_bad_decl(f, token, file_path);
+			}
+
+			return make_import_decl(f, s->TagStmt.token, file_path, import_name, os, arch, false);
+		} else if (str_eq(tag, str_lit("load"))) {
+			String os = {0};
+			String arch = {0};
+			// TODO(bill): better error messages
+			Token file_path = expect_token(f, Token_String);
+			Token import_name = file_path;
+			import_name.string = str_lit(".");
+
+			if (f->curr_proc == NULL) {
+				return make_import_decl(f, s->TagStmt.token, file_path, import_name, os, arch, true);
+			}
+			syntax_error(token, "You cannot use #load within a procedure. This must be done at the file scope");
+			return make_bad_decl(f, token, file_path);
+		} else {
+
 		}
 
 		s->TagStmt.stmt = parse_stmt(f); // TODO(bill): Find out why this doesn't work as an argument

+ 0 - 19
src/ssa.c

@@ -2447,27 +2447,11 @@ void ssa_emit_slice_bounds_check(ssaProcedure *proc, Token token, ssaValue *low,
 		return;
 	}
 
-
 	low  = ssa_emit_conv(proc, low,  t_int);
 	high = ssa_emit_conv(proc, high, t_int);
 	max  = ssa_emit_conv(proc, max,  t_int);
 
 	ssa_emit(proc, ssa_make_instr_slice_bounds_check(proc, token.pos, low, high, max, is_substring));
-
-	// gbAllocator a = proc->module->allocator;
-	// ssaValue **args = gb_alloc_array(a, ssaValue *, 6);
-	// args[0] = ssa_emit_global_string(proc, token.pos.file);
-	// args[1] = ssa_make_const_int(a, token.pos.line);
-	// args[2] = ssa_make_const_int(a, token.pos.column);
-	// args[3] = ssa_emit_conv(proc, low, t_int);
-	// args[4] = ssa_emit_conv(proc, high, t_int);
-	// args[5] = ssa_emit_conv(proc, max, t_int);
-
-	// if (!is_substring) {
-	// 	ssa_emit_global_call(proc, "__slice_expr_error", args, 6);
-	// } else {
-	// 	ssa_emit_global_call(proc, "__substring_expr_error", args, 5);
-	// }
 }
 
 
@@ -2878,7 +2862,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					Type *elem_type = slice_type->Slice.elem;
 					i64 size_of_elem = type_size_of(proc->module->sizes, proc->module->allocator, elem_type);
 
-
 					ssaValue *dst = ssa_emit_conv(proc, ssa_slice_elem(proc, dst_slice), t_rawptr);
 					ssaValue *src = ssa_emit_conv(proc, ssa_slice_elem(proc, src_slice), t_rawptr);
 
@@ -3031,7 +3014,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					ssa_emit_comment(proc, str_lit("min"));
 					ssaValue *x = ssa_build_expr(proc, ce->args.e[0]);
 					ssaValue *y = ssa_build_expr(proc, ce->args.e[1]);
-					Type *t = base_type(ssa_type(x));
 					ssaValue *cond = ssa_emit_comp(proc, Token_Lt, x, y);
 					return ssa_emit_select(proc, cond, x, y);
 				} break;
@@ -3040,7 +3022,6 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue
 					ssa_emit_comment(proc, str_lit("max"));
 					ssaValue *x = ssa_build_expr(proc, ce->args.e[0]);
 					ssaValue *y = ssa_build_expr(proc, ce->args.e[1]);
-					Type *t = base_type(ssa_type(x));
 					ssaValue *cond = ssa_emit_comp(proc, Token_Gt, x, y);
 					return ssa_emit_select(proc, cond, x, y);
 				} break;

+ 107 - 80
src/string.c

@@ -52,7 +52,8 @@ gb_inline String make_string_c(char *text) {
 
 gb_inline bool str_eq_ignore_case(String a, String b) {
 	if (a.len == b.len) {
-		for (isize i = 0; i < a.len; i++) {
+		isize i;
+		for (i = 0; i < a.len; i++) {
 			char x = cast(char)a.text[i];
 			char y = cast(char)b.text[i];
 			if (gb_char_to_lower(x) != gb_char_to_lower(y))
@@ -64,39 +65,40 @@ gb_inline bool str_eq_ignore_case(String a, String b) {
 }
 
 int string_compare(String x, String y) {
-	if (x.len == y.len &&
-	    x.text == y.text) {
-		return 0;
-	}
-
-	isize n = gb_min(x.len, y.len);
-
-	isize fast = n/gb_size_of(isize) + 1;
-	isize offset = (fast-1)*gb_size_of(isize);
-	isize curr_block = 0;
-	if (n <= gb_size_of(isize)) {
-		fast = 0;
-	}
+	if (!(x.len == y.len &&
+	      x.text == y.text)) {
+		isize n, fast, offset, curr_block;
+		isize *la, *lb;
+		isize pos;
+
+		n = gb_min(x.len, y.len);
+
+		fast = n/gb_size_of(isize) + 1;
+		offset = (fast-1)*gb_size_of(isize);
+		curr_block = 0;
+		if (n <= gb_size_of(isize)) {
+			fast = 0;
+		}
 
-	isize *la = cast(isize *)x.text;
-	isize *lb = cast(isize *)y.text;
+		la = cast(isize *)x.text;
+		lb = cast(isize *)y.text;
 
-	for (; curr_block < fast; curr_block++) {
-		if (la[curr_block] ^ lb[curr_block]) {
-			for (isize pos = curr_block*gb_size_of(isize); pos < n; pos++) {
-				if (x.text[pos] ^ y.text[pos]) {
-					return cast(int)x.text[pos] - cast(int)y.text[pos];
+		for (; curr_block < fast; curr_block++) {
+			if (la[curr_block] ^ lb[curr_block]) {
+				for (pos = curr_block*gb_size_of(isize); pos < n; pos++) {
+					if (x.text[pos] ^ y.text[pos]) {
+						return cast(int)x.text[pos] - cast(int)y.text[pos];
+					}
 				}
 			}
 		}
-	}
 
-	for (; offset < n; offset++) {
-		if (x.text[offset] ^ y.text[offset]) {
-			return cast(int)x.text[offset] - cast(int)y.text[offset];
+		for (; offset < n; offset++) {
+			if (x.text[offset] ^ y.text[offset]) {
+				return cast(int)x.text[offset] - cast(int)y.text[offset];
+			}
 		}
 	}
-
 	return 0;
 }
 
@@ -126,7 +128,18 @@ gb_inline bool str_gt(String a, String b) { return string_compare(a, b) > 0;
 gb_inline bool str_le(String a, String b) { return string_compare(a, b) <= 0;    }
 gb_inline bool str_ge(String a, String b) { return string_compare(a, b) >= 0;    }
 
-
+gb_inline bool str_has_prefix(String s, String prefix) {
+	isize i;
+	if (prefix.len < s.len) {
+		return false;
+	}
+	for (i = 0; i < prefix.len; i++) {
+		if (s.text[i] != prefix.text[i]) {
+			return false;
+		}
+	}
+	return true;
+}
 
 gb_inline isize string_extension_position(String str) {
 	isize dot_pos = -1;
@@ -157,7 +170,8 @@ gb_inline bool string_has_extension(String str, String ext) {
 }
 
 bool string_contains_char(String s, u8 c) {
-	for (isize i = 0; i < s.len; i++) {
+	isize i;
+	for (i = 0; i < s.len; i++) {
 		if (s.text[i] == c)
 			return true;
 	}
@@ -166,20 +180,23 @@ bool string_contains_char(String s, u8 c) {
 
 // TODO(bill): Make this non-windows specific
 String16 string_to_string16(gbAllocator a, String s) {
+	int len, len1;
+	wchar_t *text;
+
 	if (s.len < 1) {
 		return make_string16(NULL, 0);
 	}
 
-	int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
-	                              cast(char *)s.text, s.len, NULL, 0);
+	len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
+	                          cast(char *)s.text, s.len, NULL, 0);
 	if (len == 0) {
 		return make_string16(NULL, 0);
 	}
 
-	wchar_t *text = gb_alloc_array(a, wchar_t, len+1);
+	text = gb_alloc_array(a, wchar_t, len+1);
 
-	int len1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
-	                               cast(char *)s.text, s.len, text, len);
+	len1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
+	                           cast(char *)s.text, s.len, text, len);
 	if (len1 == 0) {
 		gb_free(a, text);
 		return make_string16(NULL, 0);
@@ -190,22 +207,23 @@ String16 string_to_string16(gbAllocator a, String s) {
 }
 
 String string16_to_string(gbAllocator a, String16 s) {
+	int len, len1;
+	u8 *text;
+
 	if (s.len < 1) {
 		return make_string(NULL, 0);
 	}
 
-	int len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
-	                              s.text, s.len, NULL, 0,
-	                              NULL, NULL);
+	len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
+	                          s.text, s.len, NULL, 0, NULL, NULL);
 	if (len == 0) {
 		return make_string(NULL, 0);
 	}
 
-	u8 *text = gb_alloc_array(a, u8, len+1);
+	text = gb_alloc_array(a, u8, len+1);
 
-	int len1 = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
-	                               s.text, s.len, cast(char *)text, len,
-	                               NULL, NULL);
+	len1 = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
+	                           s.text, s.len, cast(char *)text, len, NULL, NULL);
 	if (len1 == 0) {
 		gb_free(a, text);
 		return make_string(NULL, 0);
@@ -233,8 +251,10 @@ String string16_to_string(gbAllocator a, String16 s) {
 
 
 bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *tail_string) {
+	u8 c;
+
 	if (s.text[0] == quote &&
-	    (quote == '$' || quote == '"')) {
+	    (quote == '\'' || quote == '"')) {
 		return false;
 	} else if (s.text[0] >= 0x80) {
 		Rune r = -1;
@@ -252,7 +272,7 @@ bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *
 	if (s.len <= 1) {
 		return false;
 	}
-	u8 c = s.text[1];
+	c = s.text[1];
 	s = make_string(s.text+2, s.len-2);
 
 	switch (c) {
@@ -268,7 +288,7 @@ bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *
 	case '\\': *rune = '\\'; break;
 
 
-	case '$':
+	case '\'':
 	case '"':
 		if (c != quote) {
 			return false;
@@ -284,11 +304,12 @@ bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *
 	case '5':
 	case '6':
 	case '7': {
+		isize i;
 		i32 r = gb_digit_to_int(c);
 		if (s.len < 2) {
 			return false;
 		}
-		for (isize i = 0; i < 2; i++) {
+		for (i = 0; i < 2; i++) {
 			i32 d = gb_digit_to_int(s.text[i]);
 			if (d < 0 || d > 7) {
 				return false;
@@ -305,18 +326,18 @@ bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *
 	case 'x':
 	case 'u':
 	case 'U': {
-		isize count = 0;
+		Rune r = 0;
+		isize i, count = 0;
 		switch (c) {
 		case 'x': count = 2; break;
 		case 'u': count = 4; break;
 		case 'U': count = 8; break;
 		}
 
-		Rune r = 0;
 		if (s.len < count) {
 			return false;
 		}
-		for (isize i = 0; i < count; i++) {
+		for (i = 0; i < count; i++) {
 			i32 d = gb_hex_digit_to_int(s.text[i]);
 			if (d < 0) {
 				return false;
@@ -344,14 +365,16 @@ bool unquote_char(String s, u8 quote, Rune *rune, bool *multiple_bytes, String *
 // 1 == original memory
 // 2 == new allocation
 i32 unquote_string(gbAllocator a, String *s_) {
-	GB_ASSERT(s_ != NULL);
 	String s = *s_;
 	isize n = s.len;
-	if (n < 2)
+	u8 quote;
+	if (n < 2) {
 		return 0;
-	u8 quote = s.text[0];
-	if (quote != s.text[n-1])
+	}
+	quote = s.text[0];
+	if (quote != s.text[n-1]) {
 		return 0;
+	}
 	s.text += 1;
 	s.len -= 2;
 
@@ -362,17 +385,19 @@ i32 unquote_string(gbAllocator a, String *s_) {
 		*s_ = s;
 		return 1;
 	}
-	if (quote != '"' && quote != '$')
+	if (quote != '"' && quote != '\'') {
 		return 0;
+	}
 
-	if (string_contains_char(s, '\n'))
+	if (string_contains_char(s, '\n')) {
 		return 0;
+	}
 
 	if (!string_contains_char(s, '\\') && !string_contains_char(s, quote)) {
 		if (quote == '"') {
 			*s_ = s;
 			return 1;
-		} else if (quote == '$') {
+		} else if (quote == '\'') {
 			Rune r = GB_RUNE_INVALID;
 			isize size = gb_utf8_decode(s.text, s.len, &r);
 			if ((size == s.len) && (r != -1 || size != 1)) {
@@ -383,34 +408,36 @@ i32 unquote_string(gbAllocator a, String *s_) {
 	}
 
 
-	u8 rune_temp[4] = {0};
-	isize buf_len = 3*s.len / 2;
-	u8 *buf = gb_alloc_array(a, u8, buf_len);
-	isize offset = 0;
-	while (s.len > 0) {
-		String tail_string = {0};
-		Rune r = 0;
-		bool multiple_bytes = false;
-		bool success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string);
-		if (!success) {
-			gb_free(a, buf);
-			return 0;
-		}
-		s = tail_string;
-
-		if (r < 0x80 || !multiple_bytes) {
-			buf[offset++] = cast(u8)r;
-		} else {
-			isize size = gb_utf8_encode_rune(rune_temp, r);
-			gb_memmove(buf+offset, rune_temp, size);
-			offset += size;
-		}
+	{
+		u8 rune_temp[4] = {0};
+		isize buf_len = 3*s.len / 2;
+		u8 *buf = gb_alloc_array(a, u8, buf_len);
+		isize offset = 0;
+		while (s.len > 0) {
+			String tail_string = {0};
+			Rune r = 0;
+			bool multiple_bytes = false;
+			bool success = unquote_char(s, quote, &r, &multiple_bytes, &tail_string);
+			if (!success) {
+				gb_free(a, buf);
+				return 0;
+			}
+			s = tail_string;
+
+			if (r < 0x80 || !multiple_bytes) {
+				buf[offset++] = cast(u8)r;
+			} else {
+				isize size = gb_utf8_encode_rune(rune_temp, r);
+				gb_memmove(buf+offset, rune_temp, size);
+				offset += size;
+			}
 
-		if (quote == '$' && s.len != 0) {
-			gb_free(a, buf);
-			return 0;
+			if (quote == '\'' && s.len != 0) {
+				gb_free(a, buf);
+				return 0;
+			}
 		}
+		*s_ = make_string(buf, offset);
 	}
-	*s_ = make_string(buf, offset);
 	return 2;
 }

+ 4 - 3
src/timings.c

@@ -77,12 +77,13 @@ f64 time_stamp_as_ms(TimeStamp ts, u64 freq) {
 }
 
 void timings_print_all(Timings *t) {
+	char const SPACES[] = "                                                                ";
+	isize max_len, i;
+
 	timings__stop_current_section(t);
 	t->total.finish = time_stamp_time_now();
 
-	char const SPACES[] = "                                                                ";
-
-	isize max_len = t->total.label.len;
+	max_len = t->total.label.len;
 	for_array(i, t->sections) {
 		TimeStamp ts = t->sections.e[i];
 		max_len = gb_max(max_len, ts.label.len);

+ 46 - 13
src/tokenizer.c

@@ -34,9 +34,6 @@ TOKEN_KIND(Token__OperatorBegin, "_OperatorBegin"), \
 	TOKEN_KIND(Token_transmute,  "transmute"), \
 	TOKEN_KIND(Token_down_cast,  "down_cast"), \
 	TOKEN_KIND(Token_union_cast, "union_cast"), \
-\
-	TOKEN_KIND(Token_Prime, "'"), \
-	TOKEN_KIND(Token_DoublePrime, "''"), \
 \
 	TOKEN_KIND(Token_CmpAnd, "&&"), \
 	TOKEN_KIND(Token_CmpOr, "||"), \
@@ -656,17 +653,50 @@ Token tokenizer_get_token(Tokenizer *t) {
 			token.kind = Token_EOF;
 			break;
 
-		case '\'':
-			token.kind = Token_Prime;
-			if (t->curr_rune == '\'') {
+		case '\'': // Rune Literal
+		{
+			token.kind = Token_Rune;
+			Rune quote = curr_rune;
+			bool valid = true;
+			i32 n = 0, success;
+			for (;;) {
+				Rune r = t->curr_rune;
+				if (r == '\n' || r < 0) {
+					tokenizer_err(t, "Rune literal not terminated");
+					break;
+				}
 				advance_to_next_rune(t);
-				token.kind = Token_DoublePrime;
+				if (r == quote) {
+					break;
+				}
+				n++;
+				if (r == '\\') {
+					if (!scan_escape(t, quote)) {
+						valid = false;
+					}
+				}
 			}
-			break;
+
+			// TODO(bill): Better Error Handling
+			if (valid && n != 1) {
+				tokenizer_err(t, "Invalid rune literal");
+			}
+			token.string.len = t->curr - token.string.text;
+			success = unquote_string(heap_allocator(), &token.string);
+			if (success > 0) {
+				if (success == 2) {
+					array_add(&t->allocated_strings, token.string);
+				}
+				return token;
+			} else {
+				tokenizer_err(t, "Invalid rune literal");
+			}
+		} break;
 
 		case '`': // Raw String Literal
 		case '"': // String Literal
 		{
+			i32 success;
 			Rune quote = curr_rune;
 			token.kind = Token_String;
 			if (curr_rune == '"') {
@@ -677,10 +707,12 @@ Token tokenizer_get_token(Tokenizer *t) {
 						break;
 					}
 					advance_to_next_rune(t);
-					if (r == quote)
+					if (r == quote) {
 						break;
-					if (r == '\\')
-						scan_escape(t, '"');
+					}
+					if (r == '\\') {
+						scan_escape(t, quote);
+					}
 				}
 			} else {
 				for (;;) {
@@ -690,12 +722,13 @@ Token tokenizer_get_token(Tokenizer *t) {
 						break;
 					}
 					advance_to_next_rune(t);
-					if (r == quote)
+					if (r == quote) {
 						break;
+					}
 				}
 			}
 			token.string.len = t->curr - token.string.text;
-			i32 success = unquote_string(heap_allocator(), &token.string);
+			success = unquote_string(heap_allocator(), &token.string);
 			if (success > 0) {
 				if (success == 2) {
 					array_add(&t->allocated_strings, token.string);

+ 1 - 1
src/unicode.c

@@ -42,10 +42,10 @@ bool rune_is_whitespace(Rune r) {
 
 
 bool is_string_an_identifier(String s) {
+	isize offset = 0;
 	if (s.len < 1) {
 		return false;
 	}
-	isize offset = 0;
 	while (offset < s.len) {
 		bool ok = false;
 		Rune r = -1;