Browse Source

Fix match statements for the new AstNodeArray type

Ginger Bill 9 years ago
parent
commit
56dd12c54c

+ 8 - 8
examples/basic.odin

@@ -183,10 +183,10 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any)  {
 			u: uint = 0;
 			u: uint = 0;
 			if arg.data != null {
 			if arg.data != null {
 				match info.size {
 				match info.size {
-				case 1:  u = (arg.data as ^u8)^ as uint
-				case 2:  u = (arg.data as ^u16)^ as uint
-				case 4:  u = (arg.data as ^u32)^ as uint
-				case 8:  u = (arg.data as ^u64)^ as uint
+				case 1:  u = (arg.data as ^u8)^   as uint
+				case 2:  u = (arg.data as ^u16)^  as uint
+				case 4:  u = (arg.data as ^u32)^  as uint
+				case 8:  u = (arg.data as ^u64)^  as uint
 				case 16: u = (arg.data as ^u128)^ as uint
 				case 16: u = (arg.data as ^u128)^ as uint
 				}
 				}
 			}
 			}
@@ -195,10 +195,10 @@ print_any_to_buffer :: proc(buf: ^[]byte, arg: any)  {
 			v: int = 0;
 			v: int = 0;
 			if arg.data != null {
 			if arg.data != null {
 				match info.size {
 				match info.size {
-				case 1:  v = (arg.data as ^i8)^ as int
-				case 2:  v = (arg.data as ^i16)^ as int
-				case 4:  v = (arg.data as ^i32)^ as int
-				case 8:  v = (arg.data as ^i64)^ as int
+				case 1:  v = (arg.data as ^i8)^   as int
+				case 2:  v = (arg.data as ^i16)^  as int
+				case 4:  v = (arg.data as ^i32)^  as int
+				case 8:  v = (arg.data as ^i64)^  as int
 				case 16: v = (arg.data as ^i128)^ as int
 				case 16: v = (arg.data as ^i128)^ as int
 				}
 				}
 			}
 			}

+ 0 - 2
examples/demo.odin

@@ -1,7 +1,5 @@
 #load "basic.odin"
 #load "basic.odin"
-#load "math.odin"
 
 
 main :: proc() {
 main :: proc() {
-	a: any = 1
 	println(137, "Hello", 1.25, true)
 	println(137, "Hello", 1.25, true)
 }
 }

+ 10 - 9
examples/game.odin

@@ -16,9 +16,7 @@ time_now :: proc() -> f64 {
 win32_print_last_error :: proc() {
 win32_print_last_error :: proc() {
 	err_code := GetLastError() as int
 	err_code := GetLastError() as int
 	if err_code != 0 {
 	if err_code != 0 {
-		print_string("GetLastError: ")
-		print_int(err_code)
-		print_string("\n")
+		println("GetLastError:", err_code)
 	}
 	}
 }
 }
 
 
@@ -46,7 +44,8 @@ make_window :: proc(title: string, msg, height: int, window_proc: WNDPROC) -> (W
 
 
 	class_name   := "Win32-Odin-Window\x00"
 	class_name   := "Win32-Odin-Window\x00"
 	c_class_name := ^class_name[0]
 	c_class_name := ^class_name[0]
-	w.c_title = to_c_string(title)
+	// w.c_title = to_c_string(title)
+	w.c_title = "Title\x00" as []byte
 
 
 	instance := GetModuleHandleA(null)
 	instance := GetModuleHandleA(null)
 
 
@@ -59,6 +58,7 @@ make_window :: proc(title: string, msg, height: int, window_proc: WNDPROC) -> (W
 	};
 	};
 
 
 	if RegisterClassExA(^w.wc) == 0 {
 	if RegisterClassExA(^w.wc) == 0 {
+		win32_print_last_error( )
 		return w, false
 		return w, false
 	}
 	}
 
 
@@ -129,6 +129,7 @@ run_game :: proc() {
 		return DefWindowProcA(hwnd, msg, wparam, lparam)
 		return DefWindowProcA(hwnd, msg, wparam, lparam)
 	}
 	}
 
 
+
 	window, window_success := make_window("Odin Language Demo", 854, 480, win32_proc)
 	window, window_success := make_window("Odin Language Demo", 854, 480, win32_proc)
 	if !window_success {
 	if !window_success {
 		return
 		return
@@ -155,7 +156,7 @@ run_game :: proc() {
 			_ = DispatchMessageA(^msg)
 			_ = DispatchMessageA(^msg)
 		}
 		}
 
 
-		if is_key_down(VK_ESCAPE) {
+		if is_key_down(Key_Code.ESCAPE) {
 			running = false
 			running = false
 		}
 		}
 
 
@@ -163,10 +164,10 @@ run_game :: proc() {
 			SPEED :: 500
 			SPEED :: 500
 			v: Vec2
 			v: Vec2
 
 
-			if is_key_down(VK_RIGHT) { v[0] += 1 }
-			if is_key_down(VK_LEFT)  { v[0] -= 1 }
-			if is_key_down(VK_UP)    { v[1] += 1 }
-			if is_key_down(VK_DOWN)  { v[1] -= 1 }
+			if is_key_down(Key_Code.RIGHT) { v[0] += 1 }
+			if is_key_down(Key_Code.LEFT)  { v[0] -= 1 }
+			if is_key_down(Key_Code.UP)    { v[1] += 1 }
+			if is_key_down(Key_Code.DOWN)  { v[1] -= 1 }
 
 
 			v = vec2_norm0(v)
 			v = vec2_norm0(v)
 
 

+ 900 - 0
examples/old_demos/demo002.odin

@@ -0,0 +1,900 @@
+// Demo 002
+#load "basic.odin"
+#load "math.odin"
+// #load "game.odin"
+
+#thread_local tls_int: int
+
+main :: proc() {
+	// Forenotes
+
+	// Semicolons are now optional
+	// Rule for when a semicolon is expected after a statement
+	// - If the next token is not on the same line
+	// - if the next token is a closing brace }
+	// - Otherwise, a semicolon is needed
+	//
+	// Expections:
+	// for, if, match
+	// if x := thing(); x < 123 {}
+	// for i := 0; i < 123; i++ {}
+
+	// Q: Should I use the new rule or go back to the old one without optional semicolons?
+
+
+	// #thread_local - see runtime.odin and above at `tls_int`
+	// #foreign_system_library - see win32.odin
+
+	// struct_compound_literals()
+	// enumerations()
+	// variadic_procedures()
+	// new_builtins()
+	// match_statement()
+	// namespacing()
+	// subtyping()
+	// tagged_unions()
+}
+
+struct_compound_literals :: proc() {
+	Thing :: type struct {
+		id: int
+		x: f32
+		name: string
+	}
+	{
+		t1: Thing
+		t1.id = 1
+
+		t3 := Thing{}
+		t4 := Thing{1, 2, "Fred"}
+		// t5 := Thing{1, 2}
+
+		t6 := Thing{
+			name = "Tom",
+			x    = 23,
+		}
+	}
+}
+
+enumerations :: proc() {
+	{
+		Fruit :: type enum {
+			APPLE,  // 0
+			BANANA, // 1
+			PEAR,   // 2
+		}
+
+		f := Fruit.APPLE
+		// g12: int = Fruit.BANANA
+		g: int = Fruit.BANANA as int
+		// However, you can use enums are index values as _any_ integer allowed
+	}
+	{
+		Fruit1 :: type enum int {
+			APPLE,
+			BANANA,
+			PEAR,
+		}
+
+		Fruit2 :: type enum u8 {
+			APPLE,
+			BANANA,
+			PEAR,
+		}
+
+		Fruit3 :: type enum u8 {
+			APPLE = 1,
+			BANANA, // 2
+			PEAR  = 5,
+			TOMATO, // 6
+		}
+	}
+
+	// Q: remove the need for `type` if it's a record (struct/enum/raw_union/union)?
+}
+
+variadic_procedures :: proc() {
+	print_ints :: proc(args: ..int) {
+		for i := 0; i < len(args); i++ {
+			if i > 0 {
+				print_string(", ")
+			}
+			print_int(args[i])
+		}
+	}
+
+	print_ints(); // nl()
+	print_ints(1); nl()
+	print_ints(1, 2, 3); nl()
+
+	print_prefix_f32s :: proc(prefix: string, args: ..f32) {
+		print_string(prefix)
+		print_string(": ")
+		for i := 0; i < len(args); i++ {
+			if i > 0 {
+				print_string(", ")
+			}
+			print_f32(args[i])
+		}
+	}
+
+	print_prefix_f32s("a"); nl()
+	print_prefix_f32s("b", 1); nl()
+	print_prefix_f32s("c", 1, 2, 3); nl()
+
+	// Internally, the variadic procedures get allocated to an array on the stack,
+	// and this array is passed a slice
+
+	// This is first step for a `print` procedure but I do not have an `any` type
+	// yet as this requires a few other things first - i.e. introspection
+
+	// NOTE(bill): I haven't yet added the feature of expanding a slice or array into
+	// a variadic a parameter but it's pretty trivial to add
+}
+
+new_builtins :: proc() {
+	{
+		a := new(int)
+		b := new_slice(int, 12)
+		c := new_slice(int, 12, 16)
+
+		defer delete(a)
+		defer delete(b)
+		defer delete(c)
+
+		// NOTE(bill): These use the current context's allocator not the default allocator
+		// see runtime.odin
+
+		// Q: Should this be `free` rather than `delete` and should I overload it for slices too?
+
+		{
+			prev_context := context
+			defer context = prev_context
+			// Q: Should I add a `push_context` feature to the language?
+
+			context.allocator = __default_allocator()
+
+			a := new(int)
+			defer delete(a)
+
+			// Do whatever
+
+		}
+	}
+
+	{
+		a: int = 123
+		b: type_of_val(a) = 321
+
+		// NOTE(bill): This matches the current naming scheme
+		// size_of
+		// align_of
+		// offset_of
+		//
+		// size_of_val
+		// align_of_val
+		// offset_of_val
+		// type_of_val
+	}
+
+	{
+		// Compile time assert
+		COND :: true
+		assert(COND)
+		// assert(!COND)
+
+		// Runtime assert
+		x := true
+		assert(x)
+		// assert(!x)
+	}
+
+	{
+		x: ^u32 = null;
+		y := ptr_offset(x, 100)
+		z := ptr_sub(y, x)
+		w := slice_ptr(x, 12)
+		t := slice_ptr(x, 12, 16)
+
+		// NOTE(bill): These are here because I've removed:
+		// pointer arithmetic
+		// pointer indexing
+		// pointer slicing
+
+		// Reason
+
+		a: [16]int
+		a[1] = 1;
+		b := ^a
+		// Auto pointer deref
+		// consistent with record members
+		assert(b[1] == 1)
+
+		// Q: Should I add them back in at the cost of inconsitency?
+	}
+
+	{
+		a, b := -1, 2
+		print_int(min(a, b)); nl()
+		print_int(max(a, b)); nl()
+		print_int(abs(a)); nl()
+
+		// These work at compile time too
+		A :: -1
+		B :: 2
+		C :: min(A, B)
+		D :: max(A, B)
+		E :: abs(A)
+
+		print_int(C); nl()
+		print_int(D); nl()
+		print_int(E); nl()
+	}
+}
+
+
+match_statement :: proc() {
+	// NOTE(bill): `match` statements are similar to `switch` statements
+	// in other languages but there are few differences
+
+	{
+		match x := 5; x {
+		case 1: // cases must be constant expression
+			print_string("1!\n")
+			// break by default
+
+		case 2:
+			s := "2!\n"; // Each case has its own scope
+			print_string(s)
+			break // explicit break
+
+		case 3, 4: // multiple cases
+			print_string("3 or 4!\n")
+
+		case 5:
+			print_string("5!\n")
+			fallthrough // explicit fallthrough
+
+		default:
+			print_string("default!\n")
+		}
+
+
+
+		match x := 1.5; x {
+		case 1.5:
+			print_string("1.5!\n")
+			// break by default
+		case MATH_TAU:
+			print_string("τ!\n")
+		default:
+			print_string("default!\n")
+		}
+
+
+
+		match x := "Hello"; x {
+		case "Hello":
+			print_string("greeting\n")
+			// break by default
+		case "Goodbye":
+			print_string("farewell\n")
+		default:
+			print_string("???\n")
+		}
+
+
+
+
+
+
+		a := 53
+		match {
+		case a == 1:
+			print_string("one\n")
+		case a == 2:
+			print_string("a couple\n")
+		case a < 7, a == 7:
+			print_string("a few\n")
+		case a < 12: // intentional bug
+			print_string("several\n")
+		case a >= 12 && a < 100:
+			print_string("dozens\n")
+		case a >= 100 && a < 1000:
+			print_string("hundreds\n")
+		default:
+			print_string("a fuck ton\n")
+		}
+
+		// Identical to this
+
+		b := 53
+		if b == 1 {
+			print_string("one\n")
+		} else if b == 2 {
+			print_string("a couple\n")
+		} else if b < 7 || b == 7 {
+			print_string("a few\n")
+		} else if b < 12 { // intentional bug
+			print_string("several\n")
+		} else if b >= 12 && b < 100 {
+			print_string("dozens\n")
+		} else if b >= 100 && b < 1000 {
+			print_string("hundreds\n")
+		} else {
+			print_string("a fuck ton\n")
+		}
+
+		// However, match statements allow for `break` and `fallthrough` unlike
+		// an if statement
+	}
+}
+
+Vector3 :: type struct {
+	x, y, z: f32
+}
+
+print_floats :: proc(args: ..f32) {
+	for i := 0; i < len(args); i++ {
+		if i > 0 {
+			print_string(", ")
+		}
+		print_f32(args[i])
+	}
+	print_nl()
+}
+
+namespacing :: proc() {
+	{
+		Thing :: type struct {
+			x: f32
+			name: string
+		}
+
+		a: Thing
+		a.x = 3
+		{
+			Thing :: type struct {
+				y: int
+				test: bool
+			}
+
+			b: Thing // Uses this scope's Thing
+			b.test = true
+		}
+	}
+
+	{
+		Entity :: type struct {
+			Guid :: type int
+			Nested :: type struct {
+				MyInt :: type int
+				i: int
+			}
+
+			CONSTANT :: 123
+
+
+			guid:   Guid
+			name:   string
+			pos:    Vector3
+			vel:    Vector3
+			nested: Nested
+		}
+
+		guid: Entity.Guid = Entity.CONSTANT
+		i: Entity.Nested.MyInt
+
+
+
+		{
+			using Entity
+			guid: Guid = CONSTANT
+			using Nested
+			i: MyInt
+		}
+
+
+		{
+			using Entity.Nested
+			guid: Entity.Guid = Entity.CONSTANT
+			i: MyInt
+		}
+
+
+		{
+			e: Entity
+			using e
+			guid = 27832
+			name = "Bob"
+
+			print_int(e.guid as int); nl()
+			print_string(e.name); nl()
+		}
+
+		{
+			using e: Entity
+			guid = 78456
+			name = "Thing"
+
+			print_int(e.guid as int); nl()
+			print_string(e.name); nl()
+		}
+	}
+
+	{
+		Entity :: type struct {
+			Guid :: type int
+			Nested :: type struct {
+				MyInt :: type int
+				i: int
+			}
+
+			CONSTANT :: 123
+
+
+			guid:      Guid
+			name:      string
+			using pos: Vector3
+			vel:       Vector3
+			using nested: ^Nested
+		}
+
+		e := Entity{nested = new(Entity.Nested)}
+		e.x = 123
+		e.i = Entity.CONSTANT
+	}
+
+
+
+	{
+		Entity :: type struct {
+			position: Vector3
+		}
+
+		print_pos_1 :: proc(entity: ^Entity) {
+			print_string("print_pos_1: ")
+			print_floats(entity.position.x, entity.position.y, entity.position.z)
+		}
+
+		print_pos_2 :: proc(entity: ^Entity) {
+			using entity
+			print_string("print_pos_2: ")
+			print_floats(position.x, position.y, position.z)
+		}
+
+		print_pos_3 :: proc(using entity: ^Entity) {
+			print_string("print_pos_3: ")
+			print_floats(position.x, position.y, position.z)
+		}
+
+		print_pos_4 :: proc(using entity: ^Entity) {
+			using position
+			print_string("print_pos_4: ")
+			print_floats(x, y, z)
+		}
+
+		e := Entity{position = Vector3{1, 2, 3}}
+		print_pos_1(^e)
+		print_pos_2(^e)
+		print_pos_3(^e)
+		print_pos_4(^e)
+
+		// This is similar to C++'s `this` pointer that is implicit and only available in methods
+	}
+}
+
+subtyping :: proc() {
+	{
+		// C way for subtyping/subclassing
+
+		Entity :: type struct {
+			position: Vector3
+		}
+
+		Frog :: type struct {
+			entity: Entity
+			jump_height: f32
+		}
+
+		f: Frog
+		f.entity.position = Vector3{1, 2, 3}
+
+		using f.entity
+		position = Vector3{1, 2, 3}
+
+	}
+
+	{
+		// C++ way for subtyping/subclassing
+
+		Entity :: type struct {
+			position: Vector3
+		}
+
+		Frog :: type struct {
+			using entity: Entity
+			jump_height: f32
+		}
+
+		f: Frog
+		f.position = Vector3{1, 2, 3}
+
+
+		print_pos :: proc(using entity: Entity) {
+			print_string("print_pos: ")
+			print_floats(position.x, position.y, position.z)
+		}
+
+		print_pos(f.entity)
+		print_pos(f)
+
+		// Subtype Polymorphism
+	}
+
+	{
+		// More than C++ way for subtyping/subclassing
+
+		Entity :: type struct {
+			position: Vector3
+		}
+
+		Frog :: type struct {
+			jump_height: f32
+			using entity: ^Entity // Doesn't have to be first member!
+		}
+
+		f: Frog
+		f.entity = new(Entity)
+		f.position = Vector3{1, 2, 3}
+
+
+		print_pos :: proc(using entity: ^Entity) {
+			print_string("print_pos: ")
+			print_floats(position.x, position.y, position.z)
+		}
+
+		print_pos(f.entity)
+		print_pos(^f)
+		print_pos(f)
+	}
+
+	{
+		// More efficient subtyping
+
+		Entity :: type struct {
+			position: Vector3
+		}
+
+		Frog :: type struct {
+			jump_height: f32
+			using entity: ^Entity
+		}
+
+		MAX_ENTITES :: 64
+		entities: [MAX_ENTITES]Entity
+		entity_count := 0
+
+		next_entity :: proc(entities: []Entity, entity_count: ^int) -> ^Entity {
+			e := ^entities[entity_count^]
+			entity_count^++
+			return e
+		}
+
+		f: Frog
+		f.entity = next_entity(entities[:], ^entity_count)
+		f.position = Vector3{3, 4, 6}
+
+		using f.position
+		print_floats(x, y, z)
+	}
+
+	{
+		// Down casting
+
+		Entity :: type struct {
+			position: Vector3
+		}
+
+		Frog :: type struct {
+			jump_height: f32
+			using entity: Entity
+		}
+
+		f: Frog
+		f.jump_height = 564
+		e := ^f.entity
+
+		frog := e down_cast ^Frog
+		print_string("down_cast: ")
+		print_f32(frog.jump_height); nl()
+
+		// NOTE(bill): `down_cast` is unsafe and there are not check are compile time or run time
+		// Q: Should I completely remove `down_cast` as I added it in about 30 minutes
+	}
+
+	{
+		// Multiple "inheritance"/subclassing
+
+		Entity :: type struct {
+			position: Vector3
+		}
+		Climber :: type struct {
+			speed: f32
+		}
+
+		Frog :: type struct {
+			using entity:  Entity
+			using climber: Climber
+		}
+	}
+}
+
+tagged_unions :: proc() {
+	{
+		EntityKind :: type enum {
+			INVALID,
+			FROG,
+			GIRAFFE,
+			HELICOPTER,
+		}
+
+		Entity :: type struct {
+			kind: EntityKind
+			using data: raw_union {
+				frog: struct {
+					jump_height: f32
+					colour: u32
+				}
+				giraffe: struct {
+					neck_length: f32
+					spot_count: int
+				}
+				helicopter: struct {
+					blade_count: int
+					weight: f32
+					pilot_name: string
+				}
+			}
+		}
+
+		e: Entity
+		e.kind = EntityKind.FROG
+		e.frog.jump_height = 12
+
+		f: type_of_val(e.frog);
+
+		// But this is very unsafe and extremely cumbersome to write
+		// In C++, I use macros to alleviate this but it's not a solution
+	}
+
+	{
+		Entity :: type union {
+			Frog: struct {
+				jump_height: f32
+				colour: u32
+			}
+			Giraffe: struct {
+				neck_length: f32
+				spot_count: int
+			}
+			Helicopter: struct {
+				blade_count: int
+				weight: f32
+				pilot_name: string
+			}
+		}
+
+		using Entity
+		f1: Frog = Frog{12, 0xff9900}
+		f2: Entity = Frog{12, 0xff9900} // Implicit cast
+		f3 := Frog{12, 0xff9900} as Entity // Explicit cast
+
+		// f3.Frog.jump_height = 12 // There are "members" of a union
+
+
+
+		e, f, g, h: Entity
+		f = Frog{12, 0xff9900}
+		g = Giraffe{2.1, 23}
+		h = Helicopter{4, 1000, "Frank"}
+
+
+
+
+		// Requires a pointer to the union
+		// `x` will be a pointer to type of the case
+
+		match type x : ^f {
+		case Frog:
+			print_string("Frog!\n")
+			print_f32(x.jump_height); nl()
+			x.jump_height = 3
+			print_f32(x.jump_height); nl()
+		case Giraffe:
+			print_string("Giraffe!\n")
+		case Helicopter:
+			print_string("ROFLCOPTER!\n")
+		default:
+			print_string("invalid entity\n")
+		}
+
+
+		// Q: Allow for a non pointer version with takes a copy instead?
+		// Or it takes the pointer the data and not a copy
+
+
+		fp := ^f as ^Frog // Unsafe
+		print_f32(fp.jump_height); nl()
+
+
+		// Internals of a tagged union
+		/*
+			struct {
+				data: [size_of_biggest_tag]u8
+				tag_index: int
+			}
+		*/
+		// This is to allow for pointer casting if needed
+
+
+		// Advantage over subtyping version
+		MAX_ENTITES :: 64
+		entities: [MAX_ENTITES]Entity
+
+		entities[0] = Frog{}
+		entities[1] = Helicopter{}
+		// etc.
+	}
+
+
+	{
+		// Transliteration of code from this actual compiler
+		// Some stuff is missing
+		Type       :: type struct {}
+		Scope      :: type struct {}
+		Token      :: type struct {}
+		AstNode    :: type struct {}
+		ExactValue :: type struct {}
+
+		EntityKind :: type enum {
+			Invalid,
+			Constant,
+			Variable,
+			UsingVariable,
+			TypeName,
+			Procedure,
+			Builtin,
+			Count,
+		}
+
+		Entity :: type struct {
+			Guid :: type i64
+
+			kind: EntityKind
+			guid: Guid
+
+			scope: ^Scope
+			token: Token
+			type_: ^Type
+
+			using data: raw_union {
+				Constant: struct {
+					value: ExactValue
+				}
+				Variable: struct {
+					visited:   bool // Cycle detection
+					used:      bool // Variable is used
+					is_field:  bool // Is struct field
+					anonymous: bool // Variable is an anonymous
+				}
+				UsingVariable: struct {
+				}
+				TypeName: struct {
+				}
+				Procedure: struct {
+					used: bool
+				}
+				Builtin: struct {
+					id: int
+				}
+			}
+		}
+
+		// Plus all the constructing procedures that go along with them!!!!
+		// It's a nightmare
+	}
+
+	{
+		Type       :: type struct {}
+		Scope      :: type struct {}
+		Token      :: type struct {}
+		AstNode    :: type struct {}
+		ExactValue :: type struct {}
+
+
+		Entity :: type union {
+			Base :: type struct {
+				Guid :: type i64
+				guid: Guid
+
+				scope: ^Scope
+				token: Token
+				type_: ^Type
+			}
+
+
+			Constant: struct {
+				using base: Base
+				value: ExactValue
+			}
+			Variable: struct {
+				using base: Base
+				visited:   bool // Cycle detection
+				used:      bool // Variable is used
+				is_field:  bool // Is struct field
+				anonymous: bool // Variable is an anonymous
+			}
+			UsingVariable: struct {
+				using base: Base
+			}
+			TypeName: struct {
+				using base: Base
+			}
+			Procedure: struct {
+				using base: Base
+				used: bool
+			}
+			Builtin: struct {
+				using base: Base
+				id: int
+			}
+		}
+
+		using Entity
+
+		e: Entity
+
+		e = Variable{
+			base = Base{},
+			used = true,
+			anonymous = false,
+		}
+
+
+
+		// Q: Allow a "base" type to be added to a union?
+		// Or even `using` on union to get the same properties?
+	}
+
+
+	{
+		// `Raw` unions still have uses, especially for mathematic types
+
+		Vector2 :: type raw_union {
+			using xy_: struct { x, y: f32 }
+			e: [2]f32
+			v: {2}f32
+		}
+
+		Vector3 :: type raw_union {
+			using xyz_: struct { x, y, z: f32 }
+			xy: Vector2
+			e: [3]f32
+			v: {3}f32
+		}
+
+		v2: Vector2
+		v2.x = 1
+		v2.e[0] = 1
+		v2.v[0] = 1
+
+		v3: Vector3
+		v3.x = 1
+		v3.e[0] = 1
+		v3.v[0] = 1
+		v3.xy.x = 1
+	}
+}
+
+nl :: proc() { print_nl() }

+ 412 - 0
examples/old_runtime.odin

@@ -0,0 +1,412 @@
+#load "win32.odin"
+
+assume :: proc(cond: bool) #foreign "llvm.assume"
+
+__debug_trap           :: proc()           #foreign "llvm.debugtrap"
+__trap                 :: proc()           #foreign "llvm.trap"
+read_cycle_counter     :: proc() -> u64    #foreign "llvm.readcyclecounter"
+
+bit_reverse16 :: proc(b: u16) -> u16 #foreign "llvm.bitreverse.i16"
+bit_reverse32 :: proc(b: u32) -> u32 #foreign "llvm.bitreverse.i32"
+bit_reverse64 :: proc(b: u64) -> u64 #foreign "llvm.bitreverse.i64"
+
+byte_swap16 :: proc(b: u16) -> u16 #foreign "llvm.bswap.i16"
+byte_swap32 :: proc(b: u32) -> u32 #foreign "llvm.bswap.i32"
+byte_swap64 :: proc(b: u64) -> u64 #foreign "llvm.bswap.i64"
+
+fmuladd_f32 :: proc(a, b, c: f32) -> f32 #foreign "llvm.fmuladd.f32"
+fmuladd_f64 :: proc(a, b, c: f64) -> f64 #foreign "llvm.fmuladd.f64"
+
+// TODO(bill): make custom heap procedures
+heap_alloc   :: proc(len: int)   -> rawptr #foreign "malloc"
+heap_dealloc :: proc(ptr: rawptr)          #foreign "free"
+
+memory_zero :: proc(data: rawptr, len: int) {
+	d := slice_ptr(data as ^byte, len)
+	for i := 0; i < len; i++ {
+		d[i] = 0
+	}
+}
+
+memory_compare :: proc(dst, src: rawptr, len: int) -> int {
+	s1, s2: ^byte = dst, src
+	for i := 0; i < len; i++ {
+		a := ptr_offset(s1, i)^
+		b := ptr_offset(s2, i)^
+		if a != b {
+			return (a - b) as int
+		}
+	}
+	return 0
+}
+
+memory_copy :: proc(dst, src: rawptr, n: int) #inline {
+	if dst == src {
+		return
+	}
+
+	v128b :: type {4}u32
+	assert(align_of(v128b) == 16)
+
+	d, s: ^byte = dst, src
+
+	for ; s as uint % 16 != 0 && n != 0; n-- {
+		d^ = s^
+		d, s = ptr_offset(d, 1), ptr_offset(s, 1)
+	}
+
+	if d as uint % 16 == 0 {
+		for ; n >= 16; d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 {
+			(d as ^v128b)^ = (s as ^v128b)^
+		}
+
+		if n&8 != 0 {
+			(d as ^u64)^ = (s as ^u64)^
+			d, s = ptr_offset(d, 8), ptr_offset(s, 8)
+		}
+		if n&4 != 0 {
+			(d as ^u32)^ = (s as ^u32)^;
+			d, s = ptr_offset(d, 4), ptr_offset(s, 4)
+		}
+		if n&2 != 0 {
+			(d as ^u16)^ = (s as ^u16)^
+			d, s = ptr_offset(d, 2), ptr_offset(s, 2)
+		}
+		if n&1 != 0 {
+			d^ = s^
+			d, s = ptr_offset(d, 1), ptr_offset(s, 1)
+		}
+		return;
+	}
+
+	// IMPORTANT NOTE(bill): Little endian only
+	LS :: proc(a, b: u32) -> u32 #inline { return a << b }
+	RS :: proc(a, b: u32) -> u32 #inline { return a >> b }
+	/* NOTE(bill): Big endian version
+	LS :: proc(a, b: u32) -> u32 #inline { return a >> b; }
+	RS :: proc(a, b: u32) -> u32 #inline { return a << b; }
+	*/
+
+	w, x: u32
+
+	if d as uint % 4 == 1 {
+		w = (s as ^u32)^
+		d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
+		d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
+		d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
+		n -= 3
+
+		for n > 16 {
+			d32 := d as ^u32
+			s32 := ptr_offset(s, 1) as ^u32
+			x = s32^; d32^ = LS(w, 24) | RS(x, 8)
+			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
+			w = s32^; d32^ = LS(x, 24) | RS(w, 8)
+			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
+			x = s32^; d32^ = LS(w, 24) | RS(x, 8)
+			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
+			w = s32^; d32^ = LS(x, 24) | RS(w, 8)
+			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
+
+			d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16
+		}
+
+	} else if d as uint % 4 == 2 {
+		w = (s as ^u32)^
+		d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
+		d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1)
+		n -= 2
+
+		for n > 17 {
+			d32 := d as ^u32
+			s32 := ptr_offset(s, 2) as ^u32
+			x = s32^; d32^ = LS(w, 16) | RS(x, 16)
+			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
+			w = s32^; d32^ = LS(x, 16) | RS(w, 16)
+			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
+			x = s32^; d32^ = LS(w, 16) | RS(x, 16)
+			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
+			w = s32^; d32^ = LS(x, 16) | RS(w, 16)
+			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
+
+			d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16
+		}
+
+	} else if d as uint % 4 == 3 {
+		w = (s as ^u32)^
+		d^ = s^
+		n -= 1
+
+		for n > 18 {
+			d32 := d as ^u32
+			s32 := ptr_offset(s, 3) as ^u32
+			x = s32^; d32^ = LS(w, 8) | RS(x, 24)
+			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
+			w = s32^; d32^ = LS(x, 8) | RS(w, 24)
+			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
+			x = s32^; d32^ = LS(w, 8) | RS(x, 24)
+			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
+			w = s32^; d32^ = LS(x, 8) | RS(w, 24)
+			d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1)
+
+			d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16
+		}
+	}
+
+	if n&16 != 0 {
+		(d as ^v128b)^ = (s as ^v128b)^
+		d, s = ptr_offset(d, 16), ptr_offset(s, 16)
+	}
+	if n&8 != 0 {
+		(d as ^u64)^ = (s as ^u64)^
+		d, s = ptr_offset(d, 8), ptr_offset(s, 8)
+	}
+	if n&4 != 0 {
+		(d as ^u32)^ = (s as ^u32)^;
+		d, s = ptr_offset(d, 4), ptr_offset(s, 4)
+	}
+	if n&2 != 0 {
+		(d as ^u16)^ = (s as ^u16)^
+		d, s = ptr_offset(d, 2), ptr_offset(s, 2)
+	}
+	if n&1 != 0 {
+		d^  = s^
+	}
+}
+
+memory_move :: proc(dst, src: rawptr, n: int) #inline {
+	d, s: ^byte = dst, src
+	if d == s {
+		return
+	}
+	if d >= ptr_offset(s, n) || ptr_offset(d, n) <= s {
+		memory_copy(d, s, n)
+		return
+	}
+
+	// TODO(bill): Vectorize the shit out of this
+	if d < s {
+		if s as int % size_of(int) == d as int % size_of(int) {
+			for d as int % size_of(int) != 0 {
+				if n == 0 {
+					return
+				}
+				n--
+				d^ = s^
+				d, s = ptr_offset(d, 1), ptr_offset(s, 1)
+			}
+			di, si := d as ^int, s as ^int
+			for n >= size_of(int) {
+				di^ = si^
+				di, si = ptr_offset(di, 1), ptr_offset(si, 1)
+				n -= size_of(int)
+			}
+		}
+		for ; n > 0; n-- {
+			d^ = s^
+			d, s = ptr_offset(d, 1), ptr_offset(s, 1)
+		}
+	} else {
+		if s as int % size_of(int) == d as int % size_of(int) {
+			for ptr_offset(d, n) as int % size_of(int) != 0 {
+				if n == 0 {
+					return
+				}
+				n--
+				d^ = s^
+				d, s = ptr_offset(d, 1), ptr_offset(s, 1)
+			}
+			for n >= size_of(int) {
+				n -= size_of(int)
+				di := ptr_offset(d, n) as ^int
+				si := ptr_offset(s, n) as ^int
+				di^ = si^
+			}
+			for ; n > 0; n-- {
+				d^ = s^
+				d, s = ptr_offset(d, 1), ptr_offset(s, 1)
+			}
+		}
+		for n > 0 {
+			n--
+			dn := ptr_offset(d, n)
+			sn := ptr_offset(s, n)
+			dn^ = sn^
+		}
+	}
+}
+
+__string_eq :: proc(a, b: string) -> bool {
+	if len(a) != len(b) {
+		return false
+	}
+	if ^a[0] == ^b[0] {
+		return true
+	}
+	return memory_compare(^a[0], ^b[0], len(a)) == 0
+}
+
+__string_cmp :: proc(a, b : string) -> int {
+	min_len := len(a)
+	if len(b) < min_len {
+		min_len = len(b)
+	}
+	for i := 0; i < min_len; i++ {
+		x := a[i]
+		y := b[i]
+		if x < y {
+			return -1
+		} else if x > y {
+			return +1
+		}
+	}
+
+	if len(a) < len(b) {
+		return -1
+	} else if len(a) > len(b) {
+		return +1
+	}
+	return 0
+}
+
+__string_ne :: proc(a, b : string) -> bool #inline { return !__string_eq(a, b) }
+__string_lt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) < 0 }
+__string_gt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > 0 }
+__string_le :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) <= 0 }
+__string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >= 0 }
+
+
+
+
+Allocation_Mode :: type enum {
+	ALLOC,
+	DEALLOC,
+	DEALLOC_ALL,
+	RESIZE,
+}
+
+
+
+Allocator_Proc :: type proc(allocator_data: rawptr, mode: Allocation_Mode,
+                            size, alignment: int,
+                            old_memory: rawptr, old_size: int, flags: u64) -> rawptr
+
+Allocator :: type struct {
+	procedure: Allocator_Proc;
+	data:      rawptr
+}
+
+
+Context :: type struct {
+	thread_ptr: rawptr
+
+	user_data:  rawptr
+	user_index: int
+
+	allocator: Allocator
+}
+
+#thread_local context: Context
+
+DEFAULT_ALIGNMENT :: 2*size_of(int)
+
+
+__check_context :: proc() {
+	if context.allocator.procedure == null {
+		context.allocator = __default_allocator()
+	}
+	if context.thread_ptr == null {
+		// TODO(bill):
+		// context.thread_ptr = current_thread_pointer()
+	}
+}
+
+
+alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_ALIGNMENT) }
+
+alloc_align :: proc(size, alignment: int) -> rawptr #inline {
+	__check_context()
+	a := context.allocator
+	return a.procedure(a.data, Allocation_Mode.ALLOC, size, alignment, null, 0, 0)
+}
+
+dealloc :: proc(ptr: rawptr) #inline {
+	__check_context()
+	a := context.allocator
+	_ = a.procedure(a.data, Allocation_Mode.DEALLOC, 0, 0, ptr, 0, 0)
+}
+dealloc_all :: proc(ptr: rawptr) #inline {
+	__check_context()
+	a := context.allocator
+	_ = a.procedure(a.data, Allocation_Mode.DEALLOC_ALL, 0, 0, ptr, 0, 0)
+}
+
+
+resize       :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { return resize_align(ptr, old_size, new_size, DEFAULT_ALIGNMENT) }
+resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline {
+	__check_context()
+	a := context.allocator
+	return a.procedure(a.data, Allocation_Mode.RESIZE, new_size, alignment, ptr, old_size, 0)
+}
+
+
+
+default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int) -> rawptr {
+	if old_memory == null {
+		return alloc_align(new_size, alignment)
+	}
+
+	if new_size == 0 {
+		dealloc(old_memory)
+		return null
+	}
+
+	if new_size == old_size {
+		return old_memory
+	}
+
+	new_memory := alloc_align(new_size, alignment)
+	if new_memory == null {
+		return null
+	}
+
+	memory_copy(new_memory, old_memory, min(old_size, new_size));
+	dealloc(old_memory)
+	return new_memory
+}
+
+
+__default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode,
+                                 size, alignment: int,
+                                 old_memory: rawptr, old_size: int, flags: u64) -> rawptr {
+	using Allocation_Mode
+	match mode {
+	case ALLOC:
+		return heap_alloc(size)
+	case RESIZE:
+		return default_resize_align(old_memory, old_size, size, alignment)
+	case DEALLOC:
+		heap_dealloc(old_memory)
+	case DEALLOC_ALL:
+		// NOTE(bill): Does nothing
+	}
+
+	return null
+}
+
+__default_allocator :: proc() -> Allocator {
+	return Allocator{
+		__default_allocator_proc,
+		null,
+	}
+}
+
+
+
+
+__assert :: proc(msg: string) {
+	file_write(file_get_standard(File_Standard.ERROR), msg as []byte)
+	// TODO(bill): Which is better?
+	// __trap()
+	__debug_trap()
+}

+ 8 - 3
examples/runtime.odin

@@ -68,9 +68,13 @@ byte_swap64 :: proc(b: u64) -> u64 #foreign "llvm.bswap.i64"
 fmuladd_f32 :: proc(a, b, c: f32) -> f32 #foreign "llvm.fmuladd.f32"
 fmuladd_f32 :: proc(a, b, c: f32) -> f32 #foreign "llvm.fmuladd.f32"
 fmuladd_f64 :: proc(a, b, c: f64) -> f64 #foreign "llvm.fmuladd.f64"
 fmuladd_f64 :: proc(a, b, c: f64) -> f64 #foreign "llvm.fmuladd.f64"
 
 
-// TODO(bill): make custom heap procedures
-heap_alloc   :: proc(len: int)   -> rawptr #foreign "malloc"
-heap_dealloc :: proc(ptr: rawptr)          #foreign "free"
+heap_alloc   :: proc(len: int) -> rawptr {
+	return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len)
+}
+
+heap_dealloc :: proc(ptr: rawptr) {
+	_ = HeapFree(GetProcessHeap(), 0, ptr)
+}
 
 
 memory_zero :: proc(data: rawptr, len: int) {
 memory_zero :: proc(data: rawptr, len: int) {
 	llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign "llvm.memset.p0i8.i64"
 	llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign "llvm.memset.p0i8.i64"
@@ -258,6 +262,7 @@ __default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode,
 		return default_resize_align(old_memory, old_size, size, alignment)
 		return default_resize_align(old_memory, old_size, size, alignment)
 	case DEALLOC:
 	case DEALLOC:
 		heap_dealloc(old_memory)
 		heap_dealloc(old_memory)
+		return null
 	case DEALLOC_ALL:
 	case DEALLOC_ALL:
 		// NOTE(bill): Does nothing
 		// NOTE(bill): Does nothing
 	}
 	}

+ 154 - 154
examples/win32.odin

@@ -242,160 +242,160 @@ wglDeleteContext  :: proc(hglrc: HGLRC) -> BOOL #foreign
 
 
 GetAsyncKeyState :: proc(v_key: i32) -> i16 #foreign
 GetAsyncKeyState :: proc(v_key: i32) -> i16 #foreign
 
 
-is_key_down :: proc(key: int) -> bool {
+is_key_down :: proc(key: Key_Code) -> bool {
 	return GetAsyncKeyState(key as i32) < 0
 	return GetAsyncKeyState(key as i32) < 0
 }
 }
 
 
-
-VK_LBUTTON    :: 0x01
-VK_RBUTTON    :: 0x02
-VK_CANCEL     :: 0x03
-VK_MBUTTON    :: 0x04
-
-VK_BACK       :: 0x08
-VK_TAB        :: 0x09
-
-VK_CLEAR      :: 0x0C
-VK_RETURN     :: 0x0D
-
-VK_SHIFT      :: 0x10
-VK_CONTROL    :: 0x11
-VK_MENU       :: 0x12
-VK_PAUSE      :: 0x13
-VK_CAPITAL    :: 0x14
-
-VK_KANA       :: 0x15
-VK_HANGEUL    :: 0x15
-VK_HANGUL     :: 0x15
-VK_JUNJA      :: 0x17
-VK_FINAL      :: 0x18
-VK_HANJA      :: 0x19
-VK_KANJI      :: 0x19
-
-VK_ESCAPE     :: 0x1B
-
-VK_CONVERT    :: 0x1C
-VK_NONCONVERT :: 0x1D
-VK_ACCEPT     :: 0x1E
-VK_MODECHANGE :: 0x1F
-
-VK_SPACE      :: 0x20
-VK_PRIOR      :: 0x21
-VK_NEXT       :: 0x22
-VK_END        :: 0x23
-VK_HOME       :: 0x24
-VK_LEFT       :: 0x25
-VK_UP         :: 0x26
-VK_RIGHT      :: 0x27
-VK_DOWN       :: 0x28
-VK_SELECT     :: 0x29
-VK_PRINT      :: 0x2A
-VK_EXECUTE    :: 0x2B
-VK_SNAPSHOT   :: 0x2C
-VK_INSERT     :: 0x2D
-VK_DELETE     :: 0x2E
-VK_HELP       :: 0x2F
-
-VK_0 :: #rune "0"
-VK_1 :: #rune "1"
-VK_2 :: #rune "2"
-VK_3 :: #rune "3"
-VK_4 :: #rune "4"
-VK_5 :: #rune "5"
-VK_6 :: #rune "6"
-VK_7 :: #rune "7"
-VK_8 :: #rune "8"
-VK_9 :: #rune "9"
-
-VK_A :: #rune "A"
-VK_B :: #rune "B"
-VK_C :: #rune "C"
-VK_D :: #rune "D"
-VK_E :: #rune "E"
-VK_F :: #rune "F"
-VK_G :: #rune "G"
-VK_H :: #rune "H"
-VK_I :: #rune "I"
-VK_J :: #rune "J"
-VK_K :: #rune "K"
-VK_L :: #rune "L"
-VK_M :: #rune "M"
-VK_N :: #rune "N"
-VK_O :: #rune "O"
-VK_P :: #rune "P"
-VK_Q :: #rune "Q"
-VK_R :: #rune "R"
-VK_S :: #rune "S"
-VK_T :: #rune "T"
-VK_U :: #rune "U"
-VK_V :: #rune "V"
-VK_W :: #rune "W"
-VK_X :: #rune "X"
-VK_Y :: #rune "Y"
-VK_Z :: #rune "Z"
-
-VK_LWIN       :: 0x5B
-VK_RWIN       :: 0x5C
-VK_APPS       :: 0x5D
-
-VK_NUMPAD0    :: 0x60
-VK_NUMPAD1    :: 0x61
-VK_NUMPAD2    :: 0x62
-VK_NUMPAD3    :: 0x63
-VK_NUMPAD4    :: 0x64
-VK_NUMPAD5    :: 0x65
-VK_NUMPAD6    :: 0x66
-VK_NUMPAD7    :: 0x67
-VK_NUMPAD8    :: 0x68
-VK_NUMPAD9    :: 0x69
-VK_MULTIPLY   :: 0x6A
-VK_ADD        :: 0x6B
-VK_SEPARATOR  :: 0x6C
-VK_SUBTRACT   :: 0x6D
-VK_DECIMAL    :: 0x6E
-VK_DIVIDE     :: 0x6F
-VK_F1         :: 0x70
-VK_F2         :: 0x71
-VK_F3         :: 0x72
-VK_F4         :: 0x73
-VK_F5         :: 0x74
-VK_F6         :: 0x75
-VK_F7         :: 0x76
-VK_F8         :: 0x77
-VK_F9         :: 0x78
-VK_F10        :: 0x79
-VK_F11        :: 0x7A
-VK_F12        :: 0x7B
-VK_F13        :: 0x7C
-VK_F14        :: 0x7D
-VK_F15        :: 0x7E
-VK_F16        :: 0x7F
-VK_F17        :: 0x80
-VK_F18        :: 0x81
-VK_F19        :: 0x82
-VK_F20        :: 0x83
-VK_F21        :: 0x84
-VK_F22        :: 0x85
-VK_F23        :: 0x86
-VK_F24        :: 0x87
-
-VK_NUMLOCK    :: 0x90
-VK_SCROLL     :: 0x91
-
-VK_LSHIFT     :: 0xA0
-VK_RSHIFT     :: 0xA1
-VK_LCONTROL   :: 0xA2
-VK_RCONTROL   :: 0xA3
-VK_LMENU      :: 0xA4
-VK_RMENU      :: 0xA5
-VK_PROCESSKEY :: 0xE5
-VK_ATTN       :: 0xF6
-VK_CRSEL      :: 0xF7
-VK_EXSEL      :: 0xF8
-VK_EREOF      :: 0xF9
-VK_PLAY       :: 0xFA
-VK_ZOOM       :: 0xFB
-VK_NONAME     :: 0xFC
-VK_PA1        :: 0xFD
-VK_OEM_CLEAR  :: 0xFE
-
+Key_Code :: enum i32 {
+	LBUTTON    = 0x01,
+	RBUTTON    = 0x02,
+	CANCEL     = 0x03,
+	MBUTTON    = 0x04,
+
+	BACK       = 0x08,
+	TAB        = 0x09,
+
+	CLEAR      = 0x0C,
+	RETURN     = 0x0D,
+
+	SHIFT      = 0x10,
+	CONTROL    = 0x11,
+	MENU       = 0x12,
+	PAUSE      = 0x13,
+	CAPITAL    = 0x14,
+
+	KANA       = 0x15,
+	HANGEUL    = 0x15,
+	HANGUL     = 0x15,
+	JUNJA      = 0x17,
+	FINAL      = 0x18,
+	HANJA      = 0x19,
+	KANJI      = 0x19,
+
+	ESCAPE     = 0x1B,
+
+	CONVERT    = 0x1C,
+	NONCONVERT = 0x1D,
+	ACCEPT     = 0x1E,
+	MODECHANGE = 0x1F,
+
+	SPACE      = 0x20,
+	PRIOR      = 0x21,
+	NEXT       = 0x22,
+	END        = 0x23,
+	HOME       = 0x24,
+	LEFT       = 0x25,
+	UP         = 0x26,
+	RIGHT      = 0x27,
+	DOWN       = 0x28,
+	SELECT     = 0x29,
+	PRINT      = 0x2A,
+	EXECUTE    = 0x2B,
+	SNAPSHOT   = 0x2C,
+	INSERT     = 0x2D,
+	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",
+
+	LWIN       = 0x5B,
+	RWIN       = 0x5C,
+	APPS       = 0x5D,
+
+	NUMPAD0    = 0x60,
+	NUMPAD1    = 0x61,
+	NUMPAD2    = 0x62,
+	NUMPAD3    = 0x63,
+	NUMPAD4    = 0x64,
+	NUMPAD5    = 0x65,
+	NUMPAD6    = 0x66,
+	NUMPAD7    = 0x67,
+	NUMPAD8    = 0x68,
+	NUMPAD9    = 0x69,
+	MULTIPLY   = 0x6A,
+	ADD        = 0x6B,
+	SEPARATOR  = 0x6C,
+	SUBTRACT   = 0x6D,
+	DECIMAL    = 0x6E,
+	DIVIDE     = 0x6F,
+	F1         = 0x70,
+	F2         = 0x71,
+	F3         = 0x72,
+	F4         = 0x73,
+	F5         = 0x74,
+	F6         = 0x75,
+	F7         = 0x76,
+	F8         = 0x77,
+	F9         = 0x78,
+	F10        = 0x79,
+	F11        = 0x7A,
+	F12        = 0x7B,
+	F13        = 0x7C,
+	F14        = 0x7D,
+	F15        = 0x7E,
+	F16        = 0x7F,
+	F17        = 0x80,
+	F18        = 0x81,
+	F19        = 0x82,
+	F20        = 0x83,
+	F21        = 0x84,
+	F22        = 0x85,
+	F23        = 0x86,
+	F24        = 0x87,
+
+	NUMLOCK    = 0x90,
+	SCROLL     = 0x91,
+
+	LSHIFT     = 0xA0,
+	RSHIFT     = 0xA1,
+	LCONTROL   = 0xA2,
+	RCONTROL   = 0xA3,
+	LMENU      = 0xA4,
+	RMENU      = 0xA5,
+	PROCESSKEY = 0xE5,
+	ATTN       = 0xF6,
+	CRSEL      = 0xF7,
+	EXSEL      = 0xF8,
+	EREOF      = 0xF9,
+	PLAY       = 0xFA,
+	ZOOM       = 0xFB,
+	NONAME     = 0xFC,
+	PA1        = 0xFD,
+	OEM_CLEAR  = 0xFE,
+}

+ 31 - 11
src/checker/expr.cpp

@@ -57,11 +57,31 @@ void add_type_info_type(Checker *c, Type *t) {
 	}
 	}
 
 
 	map_set(&c->info.type_info_types, hash_pointer(t), t);
 	map_set(&c->info.type_info_types, hash_pointer(t), t);
+
+	if (t->kind == Type_Named) {
+		// NOTE(bill): Just in case
+		add_type_info_type(c, t->Named.base);
+		return;
+	}
+
 	Type *bt = get_base_type(t);
 	Type *bt = get_base_type(t);
 	switch (bt->kind) {
 	switch (bt->kind) {
-	case Type_Named:   add_type_info_type(c, bt->Named.base);   break;
-	case Type_Array:   add_type_info_type(c, bt->Array.elem);   break;
-	case Type_Slice:   add_type_info_type(c, bt->Slice.elem);   break;
+	case Type_Basic: {
+		if (bt->Basic.kind == Basic_string) {
+			add_type_info_type(c, make_type_pointer(c->allocator, t_u8));
+			add_type_info_type(c, t_int);
+		}
+	} break;
+	case Type_Array:
+		add_type_info_type(c, bt->Array.elem);
+		add_type_info_type(c, make_type_pointer(c->allocator, bt->Array.elem));
+		add_type_info_type(c, t_int);
+		break;
+	case Type_Slice:
+		add_type_info_type(c, bt->Slice.elem);
+		add_type_info_type(c, make_type_pointer(c->allocator, bt->Slice.elem));
+		add_type_info_type(c, t_int);
+		break;
 	case Type_Vector:  add_type_info_type(c, bt->Vector.elem);  break;
 	case Type_Vector:  add_type_info_type(c, bt->Vector.elem);  break;
 	case Type_Pointer: add_type_info_type(c, bt->Pointer.elem); break;
 	case Type_Pointer: add_type_info_type(c, bt->Pointer.elem); break;
 	case Type_Record: {
 	case Type_Record: {
@@ -1372,7 +1392,7 @@ void check_shift(Checker *c, Operand *x, Operand *y, AstNode *node) {
 	x->mode = Addressing_Value;
 	x->mode = Addressing_Value;
 }
 }
 
 
-b32 check_castable_to(Checker *c, Operand *operand, Type *y) {
+b32 check_is_castable_to(Checker *c, Operand *operand, Type *y) {
 	if (check_is_assignable_to(c, operand, y))
 	if (check_is_assignable_to(c, operand, y))
 		return true;
 		return true;
 
 
@@ -1387,20 +1407,20 @@ b32 check_castable_to(Checker *c, Operand *operand, Type *y) {
 
 
 
 
 	// Cast between booleans and integers
 	// Cast between booleans and integers
-	if (is_type_boolean(x) || is_type_integer(x)) {
-		if (is_type_boolean(y) || is_type_integer(y))
+	if (is_type_boolean(xb) || is_type_integer(xb)) {
+		if (is_type_boolean(yb) || is_type_integer(yb))
 			return true;
 			return true;
 	}
 	}
 
 
 	// Cast between numbers
 	// Cast between numbers
-	if (is_type_integer(x) || is_type_float(x)) {
-		if (is_type_integer(y) || is_type_float(y))
+	if (is_type_integer(xb) || is_type_float(xb)) {
+		if (is_type_integer(yb) || is_type_float(yb))
 			return true;
 			return true;
 	}
 	}
 
 
 	// Cast between pointers
 	// Cast between pointers
-	if (is_type_pointer(x)) {
-		if (is_type_pointer(y))
+	if (is_type_pointer(xb)) {
+		if (is_type_pointer(yb))
 			return true;
 			return true;
 	}
 	}
 
 
@@ -1494,7 +1514,7 @@ void check_binary_expr(Checker *c, Operand *x, AstNode *node) {
 					can_convert = true;
 					can_convert = true;
 				}
 				}
 			}
 			}
-		} else if (check_castable_to(c, x, type)) {
+		} else if (check_is_castable_to(c, x, type)) {
 			x->mode = Addressing_Value;
 			x->mode = Addressing_Value;
 			can_convert = true;
 			can_convert = true;
 		}
 		}

+ 15 - 10
src/checker/type.cpp

@@ -706,8 +706,8 @@ void selection_add_index(Selection *s, isize index) {
 	gb_array_append(s->index, index);
 	gb_array_append(s->index, index);
 }
 }
 
 
-gb_global Entity *entity_any_type_info = NULL;
-gb_global Entity *entity_any_data      = NULL;
+gb_global Entity *entity__any_type_info = NULL;
+gb_global Entity *entity__any_data      = NULL;
 
 
 Selection lookup_field(Type *type_, String field_name, b32 is_type, Selection sel = empty_selection) {
 Selection lookup_field(Type *type_, String field_name, b32 is_type, Selection sel = empty_selection) {
 	GB_ASSERT(type_ != NULL);
 	GB_ASSERT(type_ != NULL);
@@ -721,29 +721,31 @@ Selection lookup_field(Type *type_, String field_name, b32 is_type, Selection se
 	type = get_base_type(type);
 	type = get_base_type(type);
 
 
 	if (type->kind == Type_Basic) {
 	if (type->kind == Type_Basic) {
-		if (type->Basic.kind == Basic_any) {
+		switch (type->Basic.kind) {
+		case Basic_any: {
 			String type_info_str = make_string("type_info");
 			String type_info_str = make_string("type_info");
 			String data_str = make_string("data");
 			String data_str = make_string("data");
-			if (entity_any_type_info == NULL) {
+			if (entity__any_type_info == NULL) {
 				Token token = {Token_Identifier};
 				Token token = {Token_Identifier};
 				token.string = type_info_str;
 				token.string = type_info_str;
-				entity_any_type_info = make_entity_field(gb_heap_allocator(), NULL, token, t_type_info_ptr, false);
+				entity__any_type_info = make_entity_field(gb_heap_allocator(), NULL, token, t_type_info_ptr, false);
 			}
 			}
-			if (entity_any_data == NULL) {
+			if (entity__any_data == NULL) {
 				Token token = {Token_Identifier};
 				Token token = {Token_Identifier};
 				token.string = data_str;
 				token.string = data_str;
-				entity_any_data = make_entity_field(gb_heap_allocator(), NULL, token, t_type_info_ptr, false);
+				entity__any_data = make_entity_field(gb_heap_allocator(), NULL, token, t_rawptr, false);
 			}
 			}
 
 
 			if (are_strings_equal(field_name, type_info_str)) {
 			if (are_strings_equal(field_name, type_info_str)) {
 				selection_add_index(&sel, 0);
 				selection_add_index(&sel, 0);
-				sel.entity = entity_any_type_info;
+				sel.entity = entity__any_type_info;
 				return sel;
 				return sel;
 			} else if (are_strings_equal(field_name, data_str)) {
 			} else if (are_strings_equal(field_name, data_str)) {
 				selection_add_index(&sel, 1);
 				selection_add_index(&sel, 1);
-				sel.entity = entity_any_data;
+				sel.entity = entity__any_data;
 				return sel;
 				return sel;
 			}
 			}
+		} break;
 		}
 		}
 
 
 		return sel;
 		return sel;
@@ -916,8 +918,11 @@ i64 type_size_of(BaseTypeSizes s, gbAllocator allocator, Type *t) {
 			if (size > 0)
 			if (size > 0)
 				return size;
 				return size;
 		}
 		}
-		if (kind == Basic_string)
+		if (kind == Basic_string) {
 			return 2 * s.word_size;
 			return 2 * s.word_size;
+		} else if (kind == Basic_any) {
+			return 2 * s.word_size;
+		}
 	} break;
 	} break;
 
 
 	case Type_Array: {
 	case Type_Array: {

+ 2 - 2
src/codegen/ssa.cpp

@@ -3184,9 +3184,9 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) {
 				fall = ssa__make_block(proc, clause, make_string("match.fall.body"));
 				fall = ssa__make_block(proc, clause, make_string("match.fall.body"));
 			}
 			}
 
 
-			if (gb_array_count(cc->list)) {
+			if (gb_array_count(cc->list) == 0) {
 				// default case
 				// default case
-				default_stmts  = cc->stmts;
+				default_stmts = cc->stmts;
 				default_fall  = fall;
 				default_fall  = fall;
 				default_block = body;
 				default_block = body;
 				continue;
 				continue;

+ 2 - 1
src/parser.cpp

@@ -78,6 +78,7 @@ enum CallExprKind {
 AstNodeArray make_ast_node_array(AstFile *f) {
 AstNodeArray make_ast_node_array(AstFile *f) {
 	AstNodeArray a;
 	AstNodeArray a;
 	gb_array_init(a, gb_arena_allocator(&f->arena));
 	gb_array_init(a, gb_arena_allocator(&f->arena));
+	GB_ASSERT(a != NULL);
 	return a;
 	return a;
 }
 }
 
 
@@ -2220,7 +2221,7 @@ AstNode *parse_for_stmt(AstFile *f) {
 
 
 AstNode *parse_case_clause(AstFile *f) {
 AstNode *parse_case_clause(AstFile *f) {
 	Token token = f->cursor[0];
 	Token token = f->cursor[0];
-	AstNodeArray list = NULL;
+	AstNodeArray list = make_ast_node_array(f);
 	if (allow_token(f, Token_case)) {
 	if (allow_token(f, Token_case)) {
 		list = parse_rhs_expr_list(f);
 		list = parse_rhs_expr_list(f);
 	} else {
 	} else {

+ 1 - 0
src/tokenizer.cpp

@@ -323,6 +323,7 @@ TokenizerInitError init_tokenizer(Tokenizer *t, String fullpath) {
 	char *c_str = gb_alloc_array(gb_heap_allocator(), char, fullpath.len+1);
 	char *c_str = gb_alloc_array(gb_heap_allocator(), char, fullpath.len+1);
 	memcpy(c_str, fullpath.text, fullpath.len);
 	memcpy(c_str, fullpath.text, fullpath.len);
 	c_str[fullpath.len] = '\0';
 	c_str[fullpath.len] = '\0';
+
 	defer (gb_free(gb_heap_allocator(), c_str));
 	defer (gb_free(gb_heap_allocator(), c_str));
 
 
 	gbFileContents fc = gb_file_read_contents(gb_heap_allocator(), true, c_str);
 	gbFileContents fc = gb_file_read_contents(gb_heap_allocator(), true, c_str);