Browse Source

Add `examples`

Ginger Bill 8 years ago
parent
commit
7cb8016df3

+ 594 - 0
examples/demo.odin

@@ -0,0 +1,594 @@
+import "fmt.odin";
+import "strconv.odin";
+import "mem.odin";
+import "thread.odin"            when ODIN_OS == "windows";
+import win32 "sys/windows.odin" when ODIN_OS == "windows";
+import "atomics.odin";
+import "bits.odin";
+import "hash.odin";
+import "math.odin";
+import "opengl.odin";
+import "os.odin";
+import "raw.odin";
+import "sort.odin";
+import "strings.odin";
+import "sync.odin";
+import "types.odin";
+import "utf8.odin";
+import "utf16.odin";
+
+general_stuff :: proc() {
+	{ // `do` for inline statmes rather than block
+		foo :: proc() do fmt.println("Foo!");
+		if   false do foo();
+		for  false do foo();
+		when false do foo();
+
+		if false do foo();
+		else     do foo();
+	}
+
+	{ // Removal of `++` and `--` (again)
+		x: int;
+		x += 1;
+		x -= 1;
+	}
+	{ // Casting syntaxes
+		i := i32(137);
+		ptr := &i;
+
+		fp1 := (^f32)(ptr);
+		// ^f32(ptr) == ^(f32(ptr))
+		fp2 := cast(^f32)ptr;
+
+		f1 := (^f32)(ptr)^;
+		f2 := (cast(^f32)ptr)^;
+
+		// Questions: Should there be two ways to do it?
+	}
+
+	/*
+	 * Remove *_val_of built-in procedures
+	 * size_of, align_of, offset_of
+	 * type_of, type_info_of
+	 */
+
+	{ // `expand_to_tuple` built-in procedure
+		Foo :: struct {
+			x: int;
+			b: bool;
+		}
+		f := Foo{137, true};
+		x, b := expand_to_tuple(f);
+		fmt.println(f);
+		fmt.println(x, b);
+		fmt.println(expand_to_tuple(f));
+	}
+
+	{
+		// ..  half-closed range
+		// ... open range
+
+		for in 0..2  {} // 0, 1
+		for in 0...2 {} // 0, 1, 2
+	}
+}
+
+nested_struct_declarations :: proc() {
+	{
+		FooInteger :: int;
+		Foo :: struct {
+			i: FooInteger;
+		};
+		f := Foo{FooInteger(137)};
+	}
+	{
+		Foo :: struct {
+			Integer :: int;
+
+			i: Integer;
+		}
+		f := Foo{Foo.Integer(137)};
+
+	}
+}
+
+default_struct_values :: proc() {
+	{
+		Vector3 :: struct {
+			x: f32;
+			y: f32;
+			z: f32;
+		}
+		v: Vector3;
+		fmt.println(v);
+	}
+	{
+		// Default values must be constants
+		Vector3 :: struct {
+			x: f32 = 1;
+			y: f32 = 4;
+			z: f32 = 9;
+		}
+		v: Vector3;
+		fmt.println(v);
+
+		v = Vector3{};
+		fmt.println(v);
+
+		// Uses the same semantics as a default values in a procedure
+		v = Vector3{137};
+		fmt.println(v);
+
+		v = Vector3{z = 137};
+		fmt.println(v);
+	}
+
+	{
+		Vector3 :: struct {
+			x := 1.0;
+			y := 4.0;
+			z := 9.0;
+		}
+		stack_default: Vector3;
+		stack_literal := Vector3{};
+		heap_one      := new(Vector3);         defer free(heap_one);
+		heap_two      := new_clone(Vector3{}); defer free(heap_two);
+
+		fmt.println("stack_default - ", stack_default);
+		fmt.println("stack_literal - ", stack_literal);
+		fmt.println("heap_one      - ", heap_one^);
+		fmt.println("heap_two      - ", heap_two^);
+
+
+		N :: 4;
+		stack_array: [N]Vector3;
+		heap_array := new([N]Vector3);    defer free(heap_array);
+		heap_slice := make([]Vector3, N); defer free(heap_slice);
+		fmt.println("stack_array[1] - ", stack_array[1]);
+		fmt.println("heap_array[1]  - ", heap_array[1]);
+		fmt.println("heap_slice[1]  - ", heap_slice[1]);
+	}
+}
+
+
+
+
+union_type :: proc() {
+	{
+		val: union{int, bool};
+		val = 137;
+		if i, ok := val.(int); ok {
+			fmt.println(i);
+		}
+		val = true;
+		fmt.println(val);
+
+		val = nil;
+
+		match v in val {
+		case int:  fmt.println("int",  v);
+		case bool: fmt.println("bool", v);
+		case:      fmt.println("nil");
+		}
+	}
+	{
+		// There is a duality between `any` and `union`
+		// An `any` has a pointer to the data and allows for any type (open)
+		// A `union` has as binary blob to store the data and allows only certain types (closed)
+		// The following code is with `any` but has the same syntax
+		val: any;
+		val = 137;
+		if i, ok := val.(int); ok {
+			fmt.println(i);
+		}
+		val = true;
+		fmt.println(val);
+
+		val = nil;
+
+		match v in val {
+		case int:  fmt.println("int",  v);
+		case bool: fmt.println("bool", v);
+		case:      fmt.println("nil");
+		}
+	}
+
+	Vector3 :: struct {
+		x, y, z: f32;
+	};
+	Quaternion :: struct {
+		x, y, z: f32;
+		w: f32 = 1;
+	};
+
+	// More realistic examples
+	{
+		// NOTE(bill): For the above basic examples, you may not have any
+		// particular use for it. However, my main use for them is not for these
+		// simple cases. My main use is for hierarchical types. Many prefer
+		// subtyping, embedding the base data into the derived types. Below is
+		// an example of this for a basic game Entity.
+
+		Entity :: struct {
+			id:          u64;
+			name:        string;
+			position:    Vector3;
+			orientation: Quaternion;
+
+			derived: any;
+		}
+
+		Frog :: struct {
+			using entity: Entity;
+			jump_height:  f32;
+		}
+
+		Monster :: struct {
+			using entity: Entity;
+			is_robot:     bool;
+			is_zombie:    bool;
+		}
+
+		// See `parametric_polymorphism` procedure for details
+		new_entity :: proc(T: type) -> ^Entity {
+			t := new(T);
+			t.derived = t^;
+			return t;
+		}
+
+		entity := new_entity(Monster);
+
+		match e in entity.derived {
+		case Frog:
+			fmt.println("Ribbit");
+		case Monster:
+			if e.is_robot  do fmt.println("Robotic");
+			if e.is_zombie do fmt.println("Grrrr!");
+		}
+	}
+
+	{
+		// NOTE(bill): A union can be used to achieve something similar. Instead
+		// of embedding the base data into the derived types, the derived data
+		// in embedded into the base type. Below is the same example of the
+		// basic game Entity but using an union.
+
+		Entity :: struct {
+			id:          u64;
+			name:        string;
+			position:    Vector3;
+			orientation: Quaternion;
+
+			derived: union {Frog, Monster};
+		}
+
+		Frog :: struct {
+			using entity: ^Entity;
+			jump_height:  f32;
+		}
+
+		Monster :: struct {
+			using entity: ^Entity;
+			is_robot:     bool;
+			is_zombie:    bool;
+		}
+
+		// See `parametric_polymorphism` procedure for details
+		new_entity :: proc(T: type) -> ^Entity {
+			t := new(Entity);
+			t.derived = T{entity = t};
+			return t;
+		}
+
+		entity := new_entity(Monster);
+
+		match e in entity.derived {
+		case Frog:
+			fmt.println("Ribbit");
+		case Monster:
+			if e.is_robot  do fmt.println("Robotic");
+			if e.is_zombie do fmt.println("Grrrr!");
+		}
+
+		// NOTE(bill): As you can see, the usage code has not changed, only its
+		// memory layout. Both approaches have their own advantages but they can
+		// be used together to achieve different results. The subtyping approach
+		// can allow for a greater control of the memory layout and memory
+		// allocation, e.g. storing the derivatives together. However, this is
+		// also its disadvantage. You must either preallocate arrays for each
+		// derivative separation (which can be easily missed) or preallocate a
+		// bunch of "raw" memory; determining the maximum size of the derived
+		// types would require the aid of metaprogramming. Unions solve this
+		// particular problem as the data is stored with the base data.
+		// Therefore, it is possible to preallocate, e.g. [100]Entity.
+
+		// It should be noted that the union approach can have the same memory
+		// layout as the any and with the same type restrictions by using a
+		// pointer type for the derivatives.
+
+		/*
+			Entity :: struct {
+				...
+				derived: union{^Frog, ^Monster};
+			}
+
+			Frog :: struct {
+				using entity: Entity;
+				...
+			}
+			Monster :: struct {
+				using entity: Entity;
+				...
+
+			}
+			new_entity :: proc(T: type) -> ^Entity {
+				t := new(T);
+				t.derived = t;
+				return t;
+			}
+		*/
+	}
+}
+
+parametric_polymorphism :: proc() {
+	print_value :: proc(value: $T) {
+		fmt.printf("print_value: %T %v\n", value, value);
+	}
+
+	v1: int    = 1;
+	v2: f32    = 2.1;
+	v3: f64    = 3.14;
+	v4: string = "message";
+
+	print_value(v1);
+	print_value(v2);
+	print_value(v3);
+	print_value(v4);
+
+	fmt.println();
+
+	add :: proc(p, q: $T) -> T {
+		x: T = p + q;
+		return x;
+	}
+
+	a := add(3, 4);
+	fmt.printf("a: %T = %v\n", a, a);
+
+	b := add(3.2, 4.3);
+	fmt.printf("b: %T = %v\n", b, b);
+
+	// This is how `new` is implemented
+	alloc_type :: proc(T: type) -> ^T {
+		t := cast(^T)alloc(size_of(T), align_of(T));
+		t^ = T{}; // Use default initialization value
+		return t;
+	}
+
+	copy_slice :: proc(dst, src: []$T) -> int {
+		n := min(len(dst), len(src));
+		if n > 0 {
+			mem.copy(&dst[0], &src[0], n*size_of(T));
+		}
+		return n;
+	}
+
+	double_params :: proc(a: $A, b: $B) -> A {
+		return a + A(b);
+	}
+
+	fmt.println(double_params(12, 1.345));
+
+
+
+	{ // Polymorphic Types and Type Specialization
+		Table :: struct(Key, Value: type) {
+			Slot :: struct {
+				occupied: bool;
+				hash:     u32;
+				key:      Key;
+				value:    Value;
+			}
+			SIZE_MIN :: 32;
+
+			count:           int;
+			allocator:       Allocator;
+			slots:           []Slot;
+		}
+
+		// Only allow types that are specializations of a (polymorphic) slice
+		make_slice :: proc(T: type/[]$E, len: int) -> T {
+			return make(T, len);
+		}
+
+
+		// Only allow types that are specializations of `Table`
+		allocate :: proc(table: ^$T/Table, capacity: int) {
+			c := context;
+			if table.allocator.procedure != nil do c.allocator = table.allocator;
+
+			push_context c {
+				table.slots = make_slice([]T.Slot, max(capacity, T.SIZE_MIN));
+			}
+		}
+
+		expand :: proc(table: ^$T/Table) {
+			c := context;
+			if table.allocator.procedure != nil do c.allocator = table.allocator;
+
+			push_context c {
+				old_slots := table.slots;
+
+				cap := max(2*cap(table.slots), T.SIZE_MIN);
+				allocate(table, cap);
+
+				for s in old_slots do if s.occupied {
+					put(table, s.key, s.value);
+				}
+
+				free(old_slots);
+			}
+		}
+
+		// Polymorphic determination of a polymorphic struct
+		// put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) {
+		put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) {
+			hash := get_hash(key); // Ad-hoc method which would fail in a different scope
+			index := find_index(table, key, hash);
+			if index < 0 {
+				if f64(table.count) >= 0.75*f64(cap(table.slots)) {
+					expand(table);
+				}
+				assert(table.count <= cap(table.slots));
+
+				hash := get_hash(key);
+				index = int(hash % u32(cap(table.slots)));
+
+				for table.slots[index].occupied {
+					if index += 1; index >= cap(table.slots) {
+						index = 0;
+					}
+				}
+
+				table.count += 1;
+			}
+
+			slot := &table.slots[index];
+			slot.occupied = true;
+			slot.hash     = hash;
+			slot.key      = key;
+			slot.value    = value;
+		}
+
+
+		// find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) {
+		find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) {
+			hash := get_hash(key);
+			index := find_index(table, key, hash);
+			if index < 0 {
+				return Value{}, false;
+			}
+			return table.slots[index].value, true;
+		}
+
+		find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int {
+			if cap(table.slots) <= 0 do return -1;
+
+			index := int(hash % u32(cap(table.slots)));
+			for table.slots[index].occupied {
+				if table.slots[index].hash == hash {
+					if table.slots[index].key == key {
+						return index;
+					}
+				}
+
+				if index += 1; index >= cap(table.slots) {
+					index = 0;
+				}
+			}
+
+			return -1;
+		}
+
+		get_hash :: proc(s: string) -> u32 { // fnv32a
+			h: u32 = 0x811c9dc5;
+			for i in 0..len(s) {
+				h = (h ~ u32(s[i])) * 0x01000193;
+			}
+			return h;
+		}
+
+
+		table: Table(string, int);
+
+		for i in 0..36 do put(&table, "Hellope", i);
+		for i in 0..42 do put(&table, "World!",  i);
+
+		found, _ := find(&table, "Hellope");
+		fmt.printf("`found` is %v\n", found);
+
+		found, _ = find(&table, "World!");
+		fmt.printf("`found` is %v\n", found);
+
+		// I would not personally design a hash table like this in production
+		// but this is a nice basic example
+		// A better approach would either use a `u64` or equivalent for the key
+		// and let the user specify the hashing function or make the user store
+		// the hashing procedure with the table
+	}
+}
+
+
+
+
+prefix_table := [...]string{
+	"White",
+	"Red",
+	"Green",
+	"Blue",
+	"Octarine",
+	"Black",
+};
+
+threading_example :: proc() {
+	when ODIN_OS == "windows" {
+		unordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) {
+			__bounds_check_error_loc(loc, index, len(array));
+			array[index] = array[len(array)-1];
+			pop(array);
+		}
+		ordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) {
+			__bounds_check_error_loc(loc, index, len(array));
+			copy(array[index..], array[index+1..]);
+			pop(array);
+		}
+
+		worker_proc :: proc(t: ^thread.Thread) -> int {
+			for iteration in 1...5 {
+				fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration);
+				fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration);
+				// win32.sleep(1);
+			}
+			return 0;
+		}
+
+		threads := make([]^thread.Thread, 0, len(prefix_table));
+		defer free(threads);
+
+		for i in 0..len(prefix_table) {
+			if t := thread.create(worker_proc); t != nil {
+				t.init_context = context;
+				t.use_init_context = true;
+				t.user_index = len(threads);
+				append(&threads, t);
+				thread.start(t);
+			}
+		}
+
+		for len(threads) > 0 {
+			for i := 0; i < len(threads); /**/ {
+				if t := threads[i]; thread.is_done(t) {
+					fmt.printf("Thread %d is done\n", t.user_index);
+					thread.destroy(t);
+
+					ordered_remove(&threads, i);
+				} else {
+					i += 1;
+				}
+			}
+		}
+	}
+}
+
+main :: proc() {
+	when false {
+		fmt.println("\n# general_stuff");              general_stuff();
+		fmt.println("\n# nested_struct_declarations"); nested_struct_declarations();
+		fmt.println("\n# default_struct_values");      default_struct_values();
+		fmt.println("\n# union_type");                 union_type();
+		fmt.println("\n# parametric_polymorphism");    parametric_polymorphism();
+		fmt.println("\n# threading_example");          threading_example();
+	}
+}
+

+ 20 - 0
examples/factorial.odin

@@ -0,0 +1,20 @@
+import "fmt.odin";
+
+main :: proc() {
+	recursive_factorial :: proc(i: u64) -> u64 {
+		if i < 2 do return 1;
+		return i * recursive_factorial(i-1);
+	}
+
+	loop_factorial :: proc(i: u64) -> u64 {
+		result: u64 = 1;
+		for n in 2..i {
+			result *= n;
+		}
+		return result;
+	}
+
+
+	fmt.println(recursive_factorial(12));
+	fmt.println(loop_factorial(12));
+}

+ 221 - 0
examples/game.odin

@@ -0,0 +1,221 @@
+import win32 "sys/windows.odin" when ODIN_OS == "windows";
+import wgl "sys/wgl.odin" when ODIN_OS == "windows";
+import "fmt.odin";
+import "math.odin";
+import "os.odin";
+import gl "opengl.odin";
+
+TWO_HEARTS :: '💕';
+
+win32_perf_count_freq := win32.get_query_performance_frequency();
+time_now :: proc() -> f64 {
+	assert(win32_perf_count_freq != 0);
+
+	counter: i64;
+	win32.query_performance_counter(&counter);
+	return f64(counter) / f64(win32_perf_count_freq);
+}
+win32_print_last_error :: proc() {
+	err_code := win32.get_last_error();
+	if err_code != 0 {
+		fmt.println("get_last_error: ", err_code);
+	}
+}
+
+// Yuk!
+to_c_string :: proc(s: string) -> []u8 {
+	c_str := make([]u8, len(s)+1);
+	copy(c_str, cast([]u8)s);
+	c_str[len(s)] = 0;
+	return c_str;
+}
+
+
+Window :: struct {
+	width, height:      int;
+	wc:                 win32.Wnd_Class_Ex_A;
+	dc:                 win32.Hdc;
+	hwnd:               win32.Hwnd;
+	opengl_context, rc: wgl.Hglrc;
+	c_title:            []u8;
+}
+
+make_window :: proc(title: string, msg, height: int, window_proc: win32.Wnd_Proc) -> (Window, bool) {
+	using win32;
+
+	w: Window;
+	w.width, w.height = msg, height;
+
+	class_name := "Win32-Odin-Window\x00";
+	c_class_name := &class_name[0];
+	if title[len(title)-1] != 0 {
+		w.c_title = to_c_string(title);
+	} else {
+		w.c_title = cast([]u8)title;
+	}
+
+	instance := get_module_handle_a(nil);
+
+	w.wc = Wnd_Class_Ex_A{
+		size       = size_of(Wnd_Class_Ex_A),
+		style      = CS_VREDRAW | CS_HREDRAW,
+		instance   = Hinstance(instance),
+		class_name = c_class_name,
+		wnd_proc   = window_proc,
+	};
+
+	if register_class_ex_a(&w.wc) == 0 {
+		win32_print_last_error();
+		return w, false;
+	}
+
+	w.hwnd = create_window_ex_a(0,
+	                            c_class_name, &w.c_title[0],
+	                            WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
+	                            CW_USEDEFAULT, CW_USEDEFAULT,
+	                            i32(w.width), i32(w.height),
+	                            nil, nil, instance, nil);
+
+	if w.hwnd == nil {
+		win32_print_last_error();
+		return w, false;
+	}
+
+	w.dc = get_dc(w.hwnd);
+
+	{
+		pfd := Pixel_Format_Descriptor{
+			size         = size_of(Pixel_Format_Descriptor),
+			version      = 1,
+			flags        = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
+			pixel_type   = PFD_TYPE_RGBA,
+			color_bits   = 32,
+			alpha_bits   = 8,
+			depth_bits   = 24,
+			stencil_bits = 8,
+			layer_type   = PFD_MAIN_PLANE,
+		};
+
+		set_pixel_format(w.dc, choose_pixel_format(w.dc, &pfd), nil);
+		w.opengl_context = wgl.create_context(w.dc);
+		wgl.make_current(w.dc, w.opengl_context);
+
+		attribs := [8]i32{
+			wgl.CONTEXT_MAJOR_VERSION_ARB, 2,
+			wgl.CONTEXT_MINOR_VERSION_ARB, 1,
+			wgl.CONTEXT_PROFILE_MASK_ARB, wgl.CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
+			0, // NOTE(bill): tells the that :: proc this is the end of attribs
+		};
+
+		wgl_str := "wglCreateContextAttribsARB\x00";
+		wglCreateContextAttribsARB := cast(wgl.Create_Context_Attribs_ARB_Type)wgl.get_proc_address(&wgl_str[0]);
+		w.rc = wglCreateContextAttribsARB(w.dc, nil, &attribs[0]);
+		wgl.make_current(w.dc, w.rc);
+		swap_buffers(w.dc);
+	}
+
+	return w, true;
+}
+
+destroy_window :: proc(w: ^Window) {
+	free(w.c_title);
+}
+
+display_window :: proc(w: ^Window) {
+	win32.swap_buffers(w.dc);
+}
+
+
+run :: proc() {
+	using math;
+
+	win32_proc :: proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline {
+		using win32;
+		if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT {
+			os.exit(0);
+			return 0;
+		}
+		return def_window_proc_a(hwnd, msg, wparam, lparam);
+	}
+
+	window, window_success := make_window("Odin Language Demo", 854, 480, cast(win32.Wnd_Proc)win32_proc);
+	if !window_success {
+		return;
+	}
+	defer destroy_window(&window);
+
+	gl.init();
+
+	using win32;
+
+	prev_time := time_now();
+	running := true;
+
+	pos := Vec2{100, 100};
+
+	for running {
+		curr_time := time_now();
+		dt := f32(curr_time - prev_time);
+		prev_time = curr_time;
+
+		msg: Msg;
+		for peek_message_a(&msg, nil, 0, 0, PM_REMOVE) > 0 {
+			if msg.message == WM_QUIT {
+				running = false;
+			}
+			translate_message(&msg);
+			dispatch_message_a(&msg);
+		}
+
+		if is_key_down(Key_Code.Escape) {
+			running = false;
+		}
+
+		{
+			SPEED :: 500;
+			v: Vec2;
+
+			if is_key_down(Key_Code.Right) do v[0] += 1;
+			if is_key_down(Key_Code.Left)  do v[0] -= 1;
+			if is_key_down(Key_Code.Up)    do v[1] += 1;
+			if is_key_down(Key_Code.Down)  do v[1] -= 1;
+
+			v = norm(v);
+
+			pos += v * Vec2{SPEED * dt};
+		}
+
+
+		gl.ClearColor(0.5, 0.7, 1.0, 1.0);
+		gl.Clear(gl.COLOR_BUFFER_BIT);
+
+		gl.LoadIdentity();
+		gl.Ortho(0, f64(window.width),
+		         0, f64(window.height), 0, 1);
+
+		draw_rect :: proc(x, y, w, h: f32) {
+			gl.Begin(gl.TRIANGLES);
+			defer gl.End();
+
+			gl.Color3f(1, 0, 0); gl.Vertex3f(x,   y,   0);
+			gl.Color3f(0, 1, 0); gl.Vertex3f(x+w, y,   0);
+			gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0);
+
+			gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0);
+			gl.Color3f(1, 1, 0); gl.Vertex3f(x,   y+h, 0);
+			gl.Color3f(1, 0, 0); gl.Vertex3f(x,   y,   0);
+		}
+
+		draw_rect(pos.x, pos.y, 50, 50);
+
+		display_window(&window);
+		if ms_to_sleep := i32(16 - 1000*dt); ms_to_sleep > 0 {
+			win32.sleep(ms_to_sleep);
+		}
+	}
+}
+
+
+main :: proc() {
+	run();
+}

+ 5 - 0
examples/hello.odin

@@ -0,0 +1,5 @@
+import "fmt.odin";
+
+main :: proc() {
+	fmt.println("Hellope, world!");
+}

+ 184 - 0
examples/http_test.odin

@@ -0,0 +1,184 @@
+import "fmt.odin";
+
+foreign_system_library ws2 "Ws2_32.lib" when ODIN_OS == "windows";
+
+
+type SOCKET uint;
+const INVALID_SOCKET = ~SOCKET(0);
+
+type AF enum i32 {
+	UNSPEC    = 0,       // unspecified
+	UNIX      = 1,       // local to host (pipes, portals)
+	INET      = 2,       // internetwork: UDP, TCP, etc.
+	IMPLINK   = 3,       // arpanet imp addresses
+	PUP       = 4,       // pup protocols: e.g. BSP
+	CHAOS     = 5,       // mit CHAOS protocols
+	NS        = 6,       // XEROX NS protocols
+	ISO       = 7,       // ISO protocols
+	OSI       = ISO,     // OSI is ISO
+	ECMA      = 8,       // european computer manufacturers
+	DATAKIT   = 9,       // datakit protocols
+	CCITT     = 10,      // CCITT protocols, X.25 etc
+	SNA       = 11,      // IBM SNA
+	DECnet    = 12,      // DECnet
+	DLI       = 13,      // Direct data link interface
+	LAT       = 14,      // LAT
+	HYLINK    = 15,      // NSC Hyperchannel
+	APPLETALK = 16,      // AppleTalk
+	ROUTE     = 17,      // Internal Routing Protocol
+	LINK      = 18,      // Link layer interface
+	XTP       = 19,      // eXpress Transfer Protocol (no AF)
+	COIP      = 20,      // connection-oriented IP, aka ST II
+	CNT       = 21,      // Computer Network Technology
+	RTIP      = 22,      // Help Identify RTIP packets
+	IPX       = 23,      // Novell Internet Protocol
+	SIP       = 24,      // Simple Internet Protocol
+	PIP       = 25,      // Help Identify PIP packets
+	MAX       = 26,
+};
+
+const (
+	SOCK_STREAM  = 1;
+	SOCKET_ERROR = -1;
+	IPPROTO_TCP  = 6;
+	AI_PASSIVE   = 0x0020;
+	SOMAXCONN    = 128;
+)
+const (
+	SD_RECEIVE = 0;
+	SD_SEND    = 1;
+	SD_BOTH    = 2;
+)
+
+const WSADESCRIPTION_LEN = 256;
+const WSASYS_STATUS_LEN  = 128;
+type WSADATA struct #ordered {
+	version:       i16,
+	high_version:  i16,
+
+
+// NOTE(bill): This is x64 ordering
+	max_sockets:   u16,
+	max_udp_dg:    u16,
+	vendor_info:   ^u8,
+	description:   [WSADESCRIPTION_LEN+1]u8,
+	system_status: [WSASYS_STATUS_LEN+1]u8,
+}
+
+type addrinfo struct #ordered {
+	flags:     i32,
+	family:    i32,
+	socktype:  i32,
+	protocol:  i32,
+	addrlen:   uint,
+	canonname: ^u8,
+	addr:      ^sockaddr,
+	next:      ^addrinfo,
+}
+
+type sockaddr struct #ordered {
+	family: u16,
+	data:   [14]u8,
+}
+
+foreign ws2 {
+	proc WSAStartup     (version_requested: i16, data: ^WSADATA) -> i32;
+	proc WSACleanup     () -> i32;
+	proc getaddrinfo    (node_name, service_name: ^u8, hints: ^addrinfo, result: ^^addrinfo) -> i32;
+	proc freeaddrinfo   (ai: ^addrinfo);
+	proc socket         (af, type_, protocol: i32) -> SOCKET;
+	proc closesocket    (s: SOCKET) -> i32;
+	proc bind           (s: SOCKET, name: ^sockaddr, name_len: i32) -> i32;
+	proc listen         (s: SOCKET, back_log: i32) -> i32;
+	proc accept         (s: SOCKET, addr: ^sockaddr, addr_len: i32) -> SOCKET;
+	proc recv           (s: SOCKET, buf: ^u8, len: i32, flags: i32) -> i32;
+	proc send           (s: SOCKET, buf: ^u8, len: i32, flags: i32) -> i32;
+	proc shutdown       (s: SOCKET, how: i32) -> i32;
+	proc WSAGetLastError() -> i32;
+}
+proc to_c_string(s: string) -> ^u8 {
+	var c_str = make([]u8, len(s)+1);
+	copy(c_str, []u8(s));
+	c_str[len(s)] = 0;
+	return &c_str[0];
+}
+
+proc run() {
+	var (
+		wsa: WSADATA;
+		res:  ^addrinfo = nil;
+		hints: addrinfo;
+		s, client: SOCKET;
+	)
+
+	if WSAStartup(2 | (2 << 8), &wsa) != 0 {
+		fmt.println("WSAStartup failed: ", WSAGetLastError());
+		return;
+	}
+	defer WSACleanup();
+
+	hints.family   = i32(AF.INET);
+	hints.socktype = SOCK_STREAM;
+	hints.protocol = IPPROTO_TCP;
+	hints.flags    = AI_PASSIVE;
+
+	if getaddrinfo(nil, to_c_string("8080"), &hints, &res) != 0 {
+		fmt.println("getaddrinfo failed: ", WSAGetLastError());
+		return;
+	}
+	defer freeaddrinfo(res);
+
+	s = socket(res.family, res.socktype, res.protocol);
+	if s == INVALID_SOCKET {
+		fmt.println("socket failed: ", WSAGetLastError());
+		return;
+	}
+	defer closesocket(s);
+
+	bind(s, res.addr, i32(res.addrlen));
+	listen(s, SOMAXCONN);
+
+	client = accept(s, nil, 0);
+	if client == INVALID_SOCKET {
+		fmt.println("socket failed: ", WSAGetLastError());
+		return;
+	}
+	defer closesocket(client);
+
+	var html =
+`HTTP/1.1 200 OK
+Connection: close
+Content-type: text/html
+
+<html>
+<head>
+	<title>Demo Title</title>
+</head>
+<body>
+	<h1 style="color: orange;">Odin Server Demo</h1>
+</body>
+</html>
+`;
+
+	var buf: [1024]u8;
+	for {
+		var bytes = recv(client, &buf[0], i32(len(buf)), 0);
+		if bytes > 0 {
+			// fmt.println(string(buf[0..<bytes]))
+			var bytes_sent = send(client, &html[0], i32(len(html)-1), 0);
+			if bytes_sent == SOCKET_ERROR {
+				fmt.println("send failed: ", WSAGetLastError());
+				return;
+			}
+			break;
+		} else if bytes == 0 {
+			fmt.println("Connection closing...");
+			break;
+		} else {
+			fmt.println("recv failed: ", WSAGetLastError());
+			return;
+		}
+	}
+
+	shutdown(client, SD_SEND);
+}

+ 595 - 0
examples/metagen.odin

@@ -0,0 +1,595 @@
+#import "fmt.odin";
+#import "os.odin";
+#import "utf8.odin";
+#import win32 "sys/windows.odin";
+
+alloc_ucs2_to_utf8 :: proc(wstr: ^u16) -> string {
+	wstr_len := 0;
+	for (wstr+wstr_len)^ != 0 {
+		wstr_len++;
+	}
+	len := 2*wstr_len-1;
+	buf := new_slice(byte, len+1);
+	str := slice_ptr(wstr, wstr_len+1);
+
+
+	i, j := 0, 0;
+	for str[j] != 0 {
+		match {
+		case str[j] < 0x80:
+			if i+1 > len {
+				return "";
+			}
+			buf[i] = cast(byte)str[j]; i++;
+			j++;
+		case str[j] < 0x800:
+			if i+2 > len {
+				return "";
+			}
+			buf[i] = cast(byte)(0xc0 + (str[j]>>6));   i++;
+			buf[i] = cast(byte)(0x80 + (str[j]&0x3f)); i++;
+			j++;
+		case 0xd800 <= str[j] && str[j] < 0xdc00:
+			if i+4 > len {
+				return "";
+			}
+			c := cast(rune)((str[j] - 0xd800) << 10) + cast(rune)((str[j+1]) - 0xdc00) + 0x10000;
+			buf[i] = cast(byte)(0xf0 +  (c >> 18));         i++;
+			buf[i] = cast(byte)(0x80 + ((c >> 12) & 0x3f)); i++;
+			buf[i] = cast(byte)(0x80 + ((c >>  6) & 0x3f)); i++;
+			buf[i] = cast(byte)(0x80 + ((c      ) & 0x3f)); i++;
+			j += 2;
+		case 0xdc00 <= str[j] && str[j] < 0xe000:
+			return "";
+		default:
+			if i+3 > len {
+				return "";
+			}
+			buf[i] = 0xe0 + cast(byte) (str[j] >> 12);         i++;
+			buf[i] = 0x80 + cast(byte)((str[j] >>  6) & 0x3f); i++;
+			buf[i] = 0x80 + cast(byte)((str[j]      ) & 0x3f); i++;
+			j++;
+		}
+	}
+
+	return cast(string)buf[..i];
+}
+
+is_whitespace :: proc(b: byte) -> bool {
+	match b {
+	case ' ', '\t', '\n', '\r', '\v', '\f':
+		return true;
+	}
+	return false;
+}
+
+is_letter :: proc(b: byte) -> bool {
+	match  {
+	case 'A' <= b && b <= 'Z',
+	     'a' <= b && b <= 'z',
+	     '_' == b:
+		return true;
+	}
+	return false;
+}
+
+is_digit :: proc(b: byte) -> bool {
+	match  {
+	case '0' <= b && b <= '9':
+		return true;
+	}
+	return false;
+}
+
+
+trim :: proc(s: string) -> string {
+	return trim_right(trim_left(s));
+}
+
+trim_left :: proc(s: string) -> string {
+	start := 0;
+	for i in 0..s.count {
+		if is_whitespace(s[i]) {
+			start++;
+		} else {
+			break;
+		}
+	}
+
+	return s[start..];
+}
+
+
+trim_right :: proc(s: string) -> string {
+	end := s.count;
+	for i := end-1; i >= 0; i-- {
+		if is_whitespace(s[i]) {
+			end--;
+		} else {
+			break;
+		}
+	}
+
+	return s[0..end];
+}
+
+errorf :: proc(format: string, args: ..any) {
+	fmt.fprintf(os.stderr, format, ..args);
+	os.exit(1);
+}
+
+errorln :: proc(args: ..any) {
+	fmt.fprintln(os.stderr, ..args);
+	os.exit(1);
+}
+
+output_filename :: proc(s: string) -> string {
+	ext := "metagen";
+	cext := "c";
+
+	i := s.count-ext.count;
+	str := new_slice(byte, i+cext.count);
+	copy(str, cast([]byte)s[..i]);
+	str[i] = 'c';
+	return cast(string)str;
+}
+
+Tokenizer :: struct {
+	filename:   string,
+	data:       []byte,
+	curr:       int,
+	read_curr:  int,
+	line:       int,
+	line_count: int,
+
+	curr_rune:   rune,
+	error_count: int,
+}
+
+Token_Kind :: enum {
+	INVALID,
+	IDENT,
+	STRING,
+	EQUAL,
+	COMMA,
+	SEMICOLON,
+	OTHER,
+	EOF,
+}
+
+Token :: struct {
+	kind:   Token_Kind,
+	text:   string,
+	line:   int,
+	column: int,
+}
+
+tokenizer_err :: proc(t: ^Tokenizer, msg: string, args: ..any) {
+	column := max(t.read_curr - t.line+1, 1);
+
+	fmt.fprintf(os.stderr, "%s(%d:%d) Syntax error: ", t.filename, t.line_count, column);
+	fmt.fprintf(os.stderr, msg, ..args);
+	fmt.fprintln(os.stderr);
+	t.error_count++;
+
+	if t.error_count > 10 {
+		os.exit(1);
+	}
+}
+
+advance_to_next_rune :: proc(t: ^Tokenizer) {
+	if t.read_curr < t.data.count {
+		t.curr = t.read_curr;
+		if t.curr_rune == '\n' {
+			t.line = t.curr;
+			t.line_count++;
+		}
+		r := cast(rune)t.data[t.read_curr];
+		width := 1;
+		if r == 0 {
+			tokenizer_err(t, "Illegal character NULL");
+		} else if r >= 0x80 {
+			r, width = utf8.decode_rune(t.data[t.read_curr..]);
+			if r == utf8.RUNE_ERROR && width == 1 {
+				tokenizer_err(t, "Illegal UTF-8 encoding");
+			} else if r == utf8.RUNE_BOM && t.curr > 0 {
+				tokenizer_err(t, "Illegal byte order mark");
+			}
+		}
+		t.read_curr += width;
+		t.curr_rune = r;
+	} else {
+		t.curr = t.data.count;
+		if t.curr_rune == '\n' {
+			t.line = t.curr;
+			t.line_count++;
+		}
+		t.curr_rune = utf8.RUNE_EOF;
+	}
+}
+
+skip_whitespace :: proc(t: ^Tokenizer) {
+	for t.curr_rune == ' ' ||
+	    t.curr_rune == '\t' ||
+	    t.curr_rune == '\n' ||
+	    t.curr_rune == '\r' {
+		advance_to_next_rune(t);
+	}
+}
+scan_escape :: proc(t: ^Tokenizer, quote: rune) -> bool {
+	advance_to_next_rune(t);
+
+	r := t.curr_rune;
+	match r {
+	case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote:
+		advance_to_next_rune(t);
+		return true;
+	default:
+		if t.curr_rune < 0 {
+			tokenizer_err(t, "Escape sequence was not terminated");
+		} else {
+			tokenizer_err(t, "Unknown espace sequence");
+		}
+		return false;
+	}
+}
+
+next_token :: proc(t: ^Tokenizer) -> Token {
+	using Token_Kind;
+	skip_whitespace(t);
+
+	token := Token{
+		line   = t.line,
+		column = t.curr-t.line+1,
+	};
+
+	prev := t.curr;
+	curr_rune := t.curr_rune;
+	if is_letter(cast(byte)t.curr_rune) {
+		for is_letter(cast(byte)t.curr_rune) || is_digit(cast(byte)t.curr_rune) {
+	    	advance_to_next_rune(t);
+	    }
+
+	    token.text = cast(string)t.data[prev..t.curr];
+	    token.kind = IDENT;
+	} else {
+		advance_to_next_rune(t);
+		token.text = cast(string)t.data[prev..t.curr];
+		match curr_rune {
+		default: token.kind = OTHER;
+		case utf8.RUNE_EOF: token.kind = EOF;
+
+		case '/':
+			if t.curr_rune != '/' {
+				token.kind = OTHER;
+			} else {
+				token.text = "";
+				for t.curr_rune != '\n' && t.curr_rune != utf8.RUNE_EOF {
+					advance_to_next_rune(t);
+				}
+				if t.curr_rune == utf8.RUNE_EOF {
+					token.kind = EOF;
+					token.text = cast(string)t.data[t.curr..t.curr+1];
+					return token;
+				}
+				return next_token(t);
+			}
+
+
+
+		case '=': token.kind = EQUAL;
+		case ',': token.kind = COMMA;
+		case ';': token.kind = SEMICOLON;
+
+		case '"':
+			for {
+				r := t.curr_rune;
+				if r == '\n' || r < 0 {
+					tokenizer_err(t, "String literal not terminated");
+					break;
+				}
+				advance_to_next_rune(t);
+				if r == '"' {
+					break;
+				}
+				if r == '\\' {
+					scan_escape(t, '"');
+				}
+			}
+
+			token.text = cast(string)t.data[prev+1..t.curr-1];
+			token.kind = STRING;
+		}
+	}
+
+	return token;
+}
+
+expect_token :: proc(t: ^Tokenizer, kind: Token_Kind) -> Token {
+	tok := next_token(t);
+	if tok.kind != kind {
+		tokenizer_err(t, "Expected %s, got %s", kind, tok.kind);
+	}
+	return tok;
+}
+
+
+alloc_command_line_arguments :: proc() -> []string {
+	arg_count: i32;
+	arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), ^arg_count);
+	arg_list := new_slice(string, arg_count);
+	for _, i in arg_list {
+		arg_list[i] = alloc_ucs2_to_utf8((arg_list_ptr+i)^);
+	}
+	return arg_list;
+}
+
+main :: proc() {
+	arg_list := alloc_command_line_arguments();
+
+	if arg_list.count < 2 {
+		errorln("Expected a .metagen file");
+		return;
+	}
+	if arg_list.count != 2 {
+		errorln("Expected only one .metagen file");
+		return;
+	}
+
+	filename := arg_list[1];
+	{ // Is extension valid?
+		i := filename.count-1;
+		for i >= 0 {
+			if filename[i] == '.' {
+				break;
+			}
+			i--;
+		}
+		if ext := filename[i..]; ext != ".metagen" {
+			errorf("Expected a .metagen file, got %s\n", filename);
+			return;
+		}
+	}
+
+	data, file_ok := os.read_entire_file(filename);
+	if !file_ok {
+		errorf("Unable to read file %s\n", filename);
+		return;
+	}
+
+	tokenizer := Tokenizer{
+		data = data,
+		filename = filename,
+		line_count = 1,
+	};
+	t := ^tokenizer;
+	advance_to_next_rune(t);
+	if t.curr_rune == utf8.RUNE_BOM {
+		advance_to_next_rune(t);
+	}
+
+	type:          string;
+	prefix:        string;
+	string_prefix: string;
+	settings_done := false;
+
+	for !settings_done {
+		using Token_Kind;
+		token := next_token(t);
+		if token.kind == Token_Kind.EOF {
+			break;
+		}
+		if token.kind != IDENT {
+			tokenizer_err(t, "Expected an identifer");
+			continue;
+		}
+		match token.text {
+		case "type", "prefix", "string":
+		default:
+			tokenizer_err(t, "Unknown setting %s", token.text);
+		}
+
+		eq := expect_token(t, EQUAL);
+		ident := expect_token(t, IDENT);
+		match token.text {
+		case "type":   type = ident.text;
+		case "prefix": prefix = ident.text;
+		case "string": string_prefix = ident.text;
+		}
+
+		expect_token(t, SEMICOLON);
+		if type != "" && prefix != "" && string_prefix != "" {
+			settings_done = true;
+		}
+	}
+
+	if !settings_done {
+		errorln("Incomplete metagen settings");
+		return;
+	}
+
+
+	new_filename := output_filename(filename);
+
+	file, file_err := os.open(new_filename, os.O_CREAT|os.O_TRUNC, 0);
+	if file_err != os.ERROR_NONE {
+		errorf("Unable to create file %s\n", new_filename);
+		return;
+	}
+	defer os.close(file);
+
+	match type {
+	case "enum":
+		Meta_Enum :: struct {
+			name:    string,
+			comment: string,
+		}
+		enums: [dynamic]Meta_Enum;
+
+		for {
+			using Token_Kind;
+			ident := next_token(t);
+			if ident.kind == EOF {
+				break;
+			}
+			if ident.kind != IDENT {
+				tokenizer_err(t, "Expected an identifer, got %s", ident.text);
+			}
+			expect_token(t, COMMA);
+			comment := expect_token(t, STRING);
+			expect_token(t, SEMICOLON);
+
+			match ident.text {
+			case "Kind", "COUNT":
+				tokenizer_err(t, "A tag cannot be called %s", ident.text);
+				continue;
+			}
+
+			append(enums, Meta_Enum{name = ident.text, comment = comment.text});
+		}
+
+		if t.error_count > 0 {
+			return;
+		}
+
+		fmt.fprintf(file, "typedef enum %sKind %sKind;\n", prefix, prefix);
+		fmt.fprintf(file, "enum %sKind {\n", prefix);
+		for e in enums {
+			fmt.fprintf(file, "\t%s_%s,\n", prefix, e.name);
+		}
+		fmt.fprintf(file, "\t%s_COUNT\n", prefix);
+		fmt.fprintf(file, "};\n");
+
+		fmt.fprintf(file, "String const %s_strings[] = {\n", string_prefix);
+		for e, i in enums {
+			fmt.fprintf(file, "\t{\"%s\", %d}", e.comment, e.comment.count);
+			if i+1 < enums.count {
+				fmt.fprint(file, ",");
+			}
+			fmt.fprintln(file, );
+		}
+		fmt.fprintf(file, "};\n\n");
+
+	case "union":
+		Meta_Union :: struct {
+			name:    string,
+			comment: string,
+			type:    string,
+		}
+
+		unions: [dynamic]Meta_Union;
+
+		for {
+			using Token_Kind;
+			ident := next_token(t);
+			if ident.kind == EOF {
+				break;
+			}
+			if ident.kind != IDENT {
+				tokenizer_err(t, "Expected an identifer, got %s", ident.text);
+			}
+			expect_token(t, COMMA);
+			comment_string := expect_token(t, STRING);
+			expect_token(t, COMMA);
+
+			brace_level := 0;
+			start := next_token(t);
+			curr := start;
+			ok := true;
+			for ok && (curr.kind != SEMICOLON || brace_level > 0) {
+				curr = next_token(t);
+				match curr.kind {
+				case EOF:
+					ok = false;
+				case OTHER:
+					match curr.text {
+					case "{": brace_level++;
+					case "}": brace_level--;
+					}
+				}
+			}
+
+			name := ident.text;
+
+			if name == "" {
+				continue;
+			}
+
+
+			if name == "Kind" {
+				tokenizer_err(t, "A tag cannot be called Kind");
+				continue;
+			}
+
+			comment := comment_string.text;
+			if comment != "" && comment[0] == '_' {
+				comment = "";
+			}
+
+			type := start.text;
+			type.count = curr.text.data - type.data;
+			type = trim(type);
+
+			append(unions, Meta_Union{name = name, comment = comment, type = type});
+		}
+
+		if t.error_count > 0 {
+			return;
+		}
+
+		fmt.fprintf(file, "typedef enum %sKind %sKind;\n", prefix, prefix);
+		fmt.fprintf(file, "enum %sKind {\n", prefix);
+		for u in unions {
+			if u.name[0] != '_' {
+				fmt.fprintf(file, "\t");
+			}
+			fmt.fprintf(file, "%s_%s,\n", prefix, u.name);
+		}
+		fmt.fprintf(file, "\t%s_COUNT\n", prefix);
+		fmt.fprintf(file, "};\n");
+
+		fmt.fprintf(file, "String const %s_strings[] = {\n", string_prefix);
+		for u, i in unions {
+			fmt.fprintf(file, "\t{\"%s\", %d}", u.comment, u.comment.count);
+			if i+1 < unions.count {
+				fmt.fprint(file, ",");
+			}
+			fmt.fprintln(file, );
+		}
+		fmt.fprintf(file, "};\n\n");
+
+
+		for u, i in unions {
+			fmt.fprintf(file, "typedef %s %s%s;\n", u.type, prefix, u.name);
+		}
+
+		fmt.fprintf(file, "\n\n");
+		fmt.fprintf(file, "struct %s{\n", prefix);
+		fmt.fprintf(file, "\t%sKind kind;\n", prefix);
+		fmt.fprintf(file, "\tunion {\n",);
+		for u, i in unions {
+			fmt.fprintf(file, "\t\t%s%s %s;\n", prefix, u.name, u.name);
+		}
+		fmt.fprintf(file, "\t};\n");
+		fmt.fprintf(file, "};\n\n");
+
+		fmt.fprintf(file,
+`
+#define %s(n_, Kind_, node_) GB_JOIN2(%s, Kind_) *n_ = &(node_)->Kind_; GB_ASSERT((node_)->kind == GB_JOIN2(%s_, Kind_))
+#define case_%s(n_, Kind_, node_) case GB_JOIN2(%s_, Kind_): { %s(n_, Kind_, node_);
+#ifndef case_end
+#define case_end } break;
+#endif
+`,
+		prefix, prefix, prefix, prefix, prefix);
+
+		fmt.fprintf(file, "\n\n");
+
+		return;
+
+	default:
+		errorf("%s is not a valid type for metagen\n", type);
+		return;
+	}
+
+}

+ 337 - 0
examples/old_demos/demo001.odin

@@ -0,0 +1,337 @@
+#import "fmt.odin";
+#import "os.odin";
+#import "mem.odin";
+// #import "http_test.odin" as ht;
+// #import "game.odin" as game;
+// #import "punity.odin" as pn;
+
+main :: proc() {
+	struct_padding();
+	bounds_checking();
+	type_introspection();
+	any_type();
+	crazy_introspection();
+	namespaces_and_files();
+	miscellany();
+
+	/*
+	ht.run();
+	game.run();
+	{
+		init :: proc(c: ^pn.Core) {}
+		step :: proc(c: ^pn.Core) {}
+
+		pn.run(init, step);
+	}
+	*/
+}
+
+struct_padding :: proc() {
+	{
+		A :: struct {
+			a: u8,
+			b: u32,
+			c: u16,
+		}
+
+		B :: struct {
+			a: [7]u8,
+			b: [3]u16,
+			c: u8,
+			d: u16,
+		}
+
+		fmt.println("size_of(A):", size_of(A));
+		fmt.println("size_of(B):", size_of(B));
+
+		// n.b. http://cbloomrants.blogspot.co.uk/2012/07/07-23-12-structs-are-not-what-you-want.html
+	}
+	{
+		A :: struct #ordered {
+			a: u8,
+			b: u32,
+			c: u16,
+		}
+
+		B :: struct #ordered {
+			a: [7]u8,
+			b: [3]u16,
+			c: u8,
+			d: u16,
+		}
+
+		fmt.println("size_of(A):", size_of(A));
+		fmt.println("size_of(B):", size_of(B));
+
+		// C-style structure layout
+	}
+	{
+		A :: struct #packed {
+			a: u8,
+			b: u32,
+			c: u16,
+		}
+
+		B :: struct #packed {
+			a: [7]u8,
+			b: [3]u16,
+			c: u8,
+			d: u16,
+		}
+
+		fmt.println("size_of(A):", size_of(A));
+		fmt.println("size_of(B):", size_of(B));
+
+		// Useful for explicit layout
+	}
+
+	// Member sorting by priority
+	// Alignment desc.
+	// Size desc.
+	// source order asc.
+
+	/*
+		A :: struct {
+			a: u8
+			b: u32
+			c: u16
+		}
+
+		B :: struct {
+			a: [7]u8
+			b: [3]u16
+			c: u8
+			d: u16
+		}
+
+		Equivalent too
+
+		A :: struct #ordered {
+			b: u32
+			c: u16
+			a: u8
+		}
+
+		B :: struct #ordered {
+			b: [3]u16
+			d: u16
+			a: [7]u8
+			c: u8
+		}
+	*/
+}
+
+bounds_checking :: proc() {
+	x: [4]int;
+	// x[-1] = 0; // Compile Time
+	// x[4]  = 0; // Compile Time
+
+	{
+		a, b := -1, 4;
+		// x[a] = 0; // Runtime Time
+		// x[b] = 0; // Runtime Time
+	}
+
+	// Works for arrays, strings, slices, and related procedures & operations
+
+	{
+		base: [10]int;
+		s := base[2..6];
+		a, b := -1, 6;
+
+		#no_bounds_check {
+			s[a] = 0;
+			// #bounds_check s[b] = 0;
+		}
+
+	#no_bounds_check
+		if s[a] == 0 {
+			// Do whatever
+		}
+
+		// Bounds checking can be toggled explicit
+		// on a per statement basis.
+		// _any statement_
+	}
+}
+
+type_introspection :: proc() {
+	{
+		info: ^Type_Info;
+		x: int;
+
+		info = type_info(int); // by type
+		info = type_info_of_val(x); // by value
+		// See: runtime.odin
+
+		match i in info {
+		case Type_Info.Integer:
+			fmt.println("integer!");
+		case Type_Info.Float:
+			fmt.println("float!");
+		default:
+			fmt.println("potato!");
+		}
+
+		// Unsafe cast
+		integer_info := cast(^Type_Info.Integer)cast(rawptr)info;
+	}
+
+	{
+		Vector2 :: struct { x, y: f32 }
+		Vector3 :: struct { x, y, z: f32 }
+
+		v1: Vector2;
+		v2: Vector3;
+		v3: Vector3;
+
+		t1 := type_info_of_val(v1);
+		t2 := type_info_of_val(v2);
+		t3 := type_info_of_val(v3);
+
+		fmt.println();
+		fmt.print("Type of v1 is:\n\t", t1);
+
+		fmt.println();
+		fmt.print("Type of v2 is:\n\t", t2);
+
+		fmt.println("\n");
+		fmt.println("t1 == t2:", t1 == t2);
+		fmt.println("t2 == t3:", t2 == t3);
+	}
+}
+
+any_type :: proc() {
+	a: any;
+
+	x: int = 123;
+	y: f64 = 6.28;
+	z: string = "Yo-Yo Ma";
+	// All types can be implicit cast to `any`
+	a = x;
+	a = y;
+	a = z;
+	a = a; // This the "identity" type, it doesn't get converted
+
+	a = 123; // Literals are copied onto the stack first
+
+	// any has two members
+	// data      - rawptr to the data
+	// type_info - pointer to the type info
+
+	fmt.println(x, y, z);
+	// See: fmt.odin
+	// For variadic any procedures in action
+}
+
+crazy_introspection :: proc() {
+	{
+		Fruit :: enum {
+			APPLE,
+			BANANA,
+			GRAPE,
+			MELON,
+			PEACH,
+			TOMATO,
+		}
+
+		s: string;
+		// s = enum_to_string(Fruit.PEACH);
+		fmt.println(s);
+
+		f := Fruit.GRAPE;
+		// s = enum_to_string(f);
+		fmt.println(s);
+
+		fmt.println(f);
+		// See: runtime.odin
+	}
+
+
+	{
+		// NOTE(bill): This is not safe code and I would not recommend this at all
+		// I'd recommend you use `match type` to get the subtype rather than
+		// casting pointers
+
+		Fruit :: enum {
+			APPLE,
+			BANANA,
+			GRAPE,
+			MELON,
+			PEACH,
+			TOMATO,
+		}
+
+		fruit_ti := type_info(Fruit);
+		name := (union_cast(^Type_Info.Named)fruit_ti).name; // Unsafe casts
+		info, _ := union_cast(^Type_Info.Enum)type_info_base(fruit_ti); // Unsafe casts
+
+		fmt.printf("%s :: enum %T {\n", name, info.base);
+		for i := 0; i < len(info.values); i++ {
+			fmt.printf("\t%s\t= %v,\n", info.names[i], info.values[i]);
+		}
+		fmt.printf("}\n");
+
+		// NOTE(bill): look at that type-safe printf!
+	}
+
+	{
+		Vector3 :: struct {x, y, z: f32}
+
+		a := Vector3{x = 1, y = 4, z = 9};
+		fmt.println(a);
+		b := Vector3{x = 9, y = 3, z = 1};
+		fmt.println(b);
+
+		// NOTE(bill): See fmt.odin
+	}
+
+	// n.b. This pretty much "solves" serialization (to strings)
+}
+
+// #import "test.odin"
+
+namespaces_and_files :: proc() {
+
+	// test.thing()
+	// test.format.println()
+	// test.println()
+	/*
+		// Non-exporting import
+		#import "file.odin"
+		#import "file.odin" as file
+		#import "file.odin" as .
+		#import "file.odin" as _
+
+		// Exporting import
+		#include "file.odin"
+	*/
+
+	// Talk about scope rules and diagram
+}
+
+miscellany :: proc() {
+	/*
+		win32 `__imp__` prefix
+		#dll_import
+		#dll_export
+
+		Change exported name/symbol for linking
+		#link_name
+
+		Custom calling conventions
+		#stdcall
+		#fastcall
+
+		Runtime stuff
+		#shared_global_scope
+	*/
+
+	// assert(false)
+	// compile_assert(false)
+	// panic("Panic message goes here")
+}
+
+
+
+

+ 892 - 0
examples/old_demos/demo002.odin

@@ -0,0 +1,892 @@
+// Demo 002
+#load "fmt.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 :: 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 :: enum {
+			APPLE,  // 0
+			BANANA, // 1
+			PEAR,   // 2
+		};
+
+		f := Fruit.APPLE;
+		// g12: int = Fruit.BANANA
+		g: int = cast(int)Fruit.BANANA;
+		// However, you can use enums are index values as _any_ integer allowed
+	}
+	{
+		Fruit1 :: enum int {
+			APPLE,
+			BANANA,
+			PEAR,
+		}
+
+		Fruit2 :: enum u8 {
+			APPLE,
+			BANANA,
+			PEAR,
+		}
+
+		Fruit3 :: 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 arg, i in args {
+			if i > 0 {
+				print(", ");
+			}
+			print(arg);
+		}
+	}
+
+	print_ints(); // nl()
+	print_ints(1); nl();
+	print_ints(1, 2, 3); nl();
+
+	print_prefix_f32s :: proc(prefix: string, args: ..f32) {
+		print(prefix);
+		print(": ");
+		for arg, i in args {
+			if i > 0 {
+				print(", ");
+			}
+			print(arg);
+		}
+	}
+
+	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 := make([]int, 12);
+		c := make([]int, 12, 16);
+
+		defer free(a);
+		defer free(b);
+		defer free(c);
+
+		// NOTE(bill): These use the current context's allocator not the default allocator
+		// see runtime.odin
+
+		// Q: Should this be `free` rather than `free` 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 free(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;
+		compile_assert(COND);
+		// compile_assert(!COND)
+
+		// Runtime assert
+		x := true;
+		assert(x);
+		// assert(!x);
+	}
+
+	{
+		x: ^u32 = nil;
+		y := x+100;
+		z := 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(min(a, b)); nl();
+		print(max(a, b)); nl();
+		print(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(C); nl();
+		print(D); nl();
+		print(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("1!\n");
+			// break by default
+
+		case 2:
+			s := "2!\n"; // Each case has its own scope
+			print(s);
+			break; // explicit break
+
+		case 3, 4: // multiple cases
+			print("3 or 4!\n");
+
+		case 5:
+			print("5!\n");
+			fallthrough; // explicit fallthrough
+
+		default:
+			print("default!\n");
+		}
+
+
+
+		match x := 1.5; x {
+		case 1.5:
+			print("1.5!\n");
+			// break by default
+		case TAU:
+			print("τ!\n");
+		default:
+			print("default!\n");
+		}
+
+
+
+		match x := "Hello"; x {
+		case "Hello":
+			print("greeting\n");
+			// break by default
+		case "Goodbye":
+			print("farewell\n");
+		default:
+			print("???\n");
+		}
+
+
+
+
+
+
+		a := 53;
+		match {
+		case a == 1:
+			print("one\n");
+		case a == 2:
+			print("a couple\n");
+		case a < 7, a == 7:
+			print("a few\n");
+		case a < 12: // intentional bug
+			print("several\n");
+		case a >= 12 && a < 100:
+			print("dozens\n");
+		case a >= 100 && a < 1000:
+			print("hundreds\n");
+		default:
+			print("a fuck ton\n");
+		}
+
+		// Identical to this
+
+		b := 53;
+		if b == 1 {
+			print("one\n");
+		} else if b == 2 {
+			print("a couple\n");
+		} else if b < 7 || b == 7 {
+			print("a few\n");
+		} else if b < 12 { // intentional bug
+			print("several\n");
+		} else if b >= 12 && b < 100 {
+			print("dozens\n");
+		} else if b >= 100 && b < 1000 {
+			print("hundreds\n");
+		} else {
+			print("a fuck ton\n");
+		}
+
+		// However, match statements allow for `break` and `fallthrough` unlike
+		// an if statement
+	}
+}
+
+Vector3 :: struct {x, y, z: f32}
+
+print_floats :: proc(args: ..f32) {
+	for arg, i in args {
+		if i > 0 {
+			print(", ");
+		}
+		print(arg);
+	}
+	println();
+}
+
+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 :: struct {
+			Guid :: int
+			Nested :: struct {
+				MyInt :: 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(e.guid as int); nl()
+			print(e.name); nl()
+		}
+
+		{
+			using e: Entity
+			guid = 78456
+			name = "Thing"
+
+			print(e.guid as int); nl()
+			print(e.name); nl()
+		}
+	}
+
+	{
+		Entity :: struct {
+			Guid :: int
+			Nested :: struct {
+				MyInt :: 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 :: struct {
+			position: Vector3
+		}
+
+		print_pos_1 :: proc(entity: ^Entity) {
+			print("print_pos_1: ");
+			print_floats(entity.position.x, entity.position.y, entity.position.z);
+		}
+
+		print_pos_2 :: proc(entity: ^Entity) {
+			using entity;
+			print("print_pos_2: ");
+			print_floats(position.x, position.y, position.z);
+		}
+
+		print_pos_3 :: proc(using entity: ^Entity) {
+			print("print_pos_3: ");
+			print_floats(position.x, position.y, position.z);
+		}
+
+		print_pos_4 :: proc(using entity: ^Entity) {
+			using position;
+			print("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 :: struct {
+			position: Vector3,
+		}
+
+		Frog :: 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 :: struct {
+			position: Vector3
+		}
+
+		Frog :: struct {
+			using entity: Entity,
+			jump_height: f32,
+		}
+
+		f: Frog;
+		f.position = Vector3{1, 2, 3};
+
+
+		print_pos :: proc(using entity: Entity) {
+			print("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 :: struct {
+			position: Vector3,
+		}
+
+		Frog :: 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("print_pos: ");
+			print_floats(position.x, position.y, position.z);
+		}
+
+		print_pos(f.entity);
+		// print_pos(^f);
+		// print_pos(f);
+	}
+
+	{
+		// More efficient subtyping
+
+		Entity :: struct {
+			position: Vector3,
+		}
+
+		Frog :: 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 :: struct {
+			position: Vector3,
+		}
+
+		Frog :: struct {
+			jump_height: f32,
+			using entity: Entity,
+		}
+
+		f: Frog;
+		f.jump_height = 564;
+		e := ^f.entity;
+
+		frog := down_cast(^Frog)e;
+		print("down_cast: ");
+		print(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 :: struct {
+			position: Vector3,
+		}
+		Climber :: struct {
+			speed: f32,
+		}
+
+		Frog :: struct {
+			using entity:  Entity,
+			using climber: Climber,
+		}
+	}
+}
+
+tagged_unions :: proc() {
+	{
+		EntityKind :: enum {
+			INVALID,
+			FROG,
+			GIRAFFE,
+			HELICOPTER,
+		}
+
+		Entity :: 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 :: union {
+			Frog{
+				jump_height: f32,
+				colour: u32,
+			},
+			Giraffe{
+				neck_length: f32,
+				spot_count: int,
+			},
+			Helicopter{
+				blade_count: int,
+				weight: f32,
+				pilot_name: string,
+			},
+		}
+
+		using Entity;
+		f1: Frog = Frog{12, 0xff9900};
+		f2: Entity = Frog{12, 0xff9900}; // Implicit cast
+		f3 := cast(Entity)Frog{12, 0xff9900}; // 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 x in ^f {
+		case Frog:
+			print("Frog!\n");
+			print(x.jump_height); nl();
+			// x.jump_height = 3;
+			print(x.jump_height); nl();
+		case Giraffe:
+			print("Giraffe!\n");
+		case Helicopter:
+			print("ROFLCOPTER!\n");
+		default:
+			print("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 := cast(^Frog)^f; // Unsafe
+		// print(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       :: struct {};
+		Scope      :: struct {};
+		Token      :: struct {};
+		AstNode    :: struct {};
+		ExactValue :: struct {};
+
+		EntityKind :: enum {
+			Invalid,
+			Constant,
+			Variable,
+			UsingVariable,
+			TypeName,
+			Procedure,
+			Builtin,
+			Count,
+		}
+
+		Guid :: i64;
+		Entity :: struct {
+
+			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       :: struct {};
+		Scope      :: struct {};
+		Token      :: struct {};
+		AstNode    :: struct {};
+		ExactValue :: struct {};
+
+
+		Guid :: i64;
+		Entity_Base :: struct {
+
+		}
+
+		Entity :: union {
+			guid: Guid,
+
+			scope: ^Scope,
+			token: Token,
+			type_: ^Type,
+
+			Constant{
+				value: ExactValue,
+			},
+			Variable{
+				visited:   bool, // Cycle detection
+				used:      bool, // Variable is used
+				is_field:  bool, // Is struct field
+				anonymous: bool, // Variable is an anonymous
+			},
+			UsingVariable{
+			},
+			TypeName{
+			},
+			Procedure{
+				used: bool,
+			},
+			Builtin{
+				id: int,
+			},
+		}
+
+		using Entity;
+
+		e: Entity;
+
+		e = Variable{
+			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 :: raw_union {
+			using xy_: struct { x, y: f32 },
+			e: [2]f32,
+			v: [vector 2]f32,
+		}
+
+		Vector3 :: raw_union {
+			using xyz_: struct { x, y, z: f32 },
+			xy: Vector2,
+			e: [3]f32,
+			v: [vector 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() { println(); }

+ 66 - 0
examples/old_demos/demo004.odin

@@ -0,0 +1,66 @@
+#import "fmt.odin";
+#import "utf8.odin";
+#import "hash.odin";
+#import "mem.odin";
+
+main :: proc() {
+	{ // New Standard Library stuff
+		s := "Hello";
+		fmt.println(s,
+		            utf8.valid_string(s),
+		            hash.murmur64(cast([]byte)s));
+
+		// utf8.odin
+		// hash.odin
+		//     - crc, fnv, fnva, murmur
+		// mem.odin
+		//     - Custom allocators
+		//     - Helpers
+	}
+
+	{
+		arena: mem.Arena;
+		mem.init_arena_from_context(^arena, mem.megabytes(16)); // Uses default allocator
+		defer mem.free_arena(^arena);
+
+		push_allocator mem.arena_allocator(^arena) {
+			x := new(int);
+			x^ = 1337;
+
+			fmt.println(x^);
+		}
+
+		/*
+			push_allocator x {
+				...
+			}
+
+			is equivalent to:
+
+			{
+				prev_allocator := __context.allocator
+				__context.allocator = x
+				defer __context.allocator = prev_allocator
+
+				...
+			}
+		*/
+
+		// You can also "push" a context
+
+		c := context; // Create copy of the allocator
+		c.allocator = mem.arena_allocator(^arena);
+
+		push_context c {
+			x := new(int);
+			x^ = 365;
+
+			fmt.println(x^);
+		}
+	}
+
+	// Backend improvements
+	// - Minimal dependency building (only build what is needed)
+	// - Numerous bugs fixed
+	// - Mild parsing recovery after bad syntax error
+}

+ 284 - 0
examples/old_demos/demo005.odin

@@ -0,0 +1,284 @@
+#import "fmt.odin";
+#import "utf8.odin";
+// #import "atomic.odin";
+// #import "hash.odin";
+// #import "math.odin";
+// #import "mem.odin";
+// #import "opengl.odin";
+// #import "os.odin";
+// #import "sync.odin";
+// #import win32 "sys/windows.odin";
+
+main :: proc() {
+	// syntax();
+	procedure_overloading();
+}
+
+syntax :: proc() {
+	// Cyclic type checking
+	// Uncomment to see the error
+	// A :: struct {b: B};
+	// B :: struct {a: A};
+
+	x: int;
+	y := cast(f32)x;
+	z := transmute(u32)y;
+	// down_cast, union_cast are similar too
+
+
+
+	// Basic directives
+	fmt.printf("Basic directives = %s(%d): %s\n", #file, #line, #procedure);
+	// NOTE: new and improved `printf`
+	// TODO: It does need accurate float printing
+
+
+
+	// record fields use the same syntax a procedure signatures
+	Thing1 :: struct {
+		x: f32,
+		y: int,
+		z: ^[]int,
+	};
+	Thing2 :: struct {x: f32, y: int, z: ^[]int};
+
+	// Slice interals are now just a `ptr+len+cap`
+	slice: []int; compile_assert(size_of_val(slice) == 3*size_of(int));
+
+	// Helper type - Help the reader understand what it is quicker
+	My_Int  :: #type int;
+	My_Proc :: #type proc(int) -> f32;
+
+
+	// All declarations with : are either variable or constant
+	// To make these declarations syntactically consistent
+	v_variable := 123;
+	c_constant :: 123;
+	c_type1    :: int;
+	c_type2    :: []int;
+	c_proc     :: proc() { /* code here */ };
+
+
+/*
+	x += 1;
+	x -= 1;
+	// ++ and -- have been removed
+	// x++;
+	// x--;
+	// Question: Should they be added again?
+	// They were removed as they are redundant and statements, not expressions
+	// like in C/C++
+*/
+
+	// You can now build files as a `.dll`
+	// `odin build_dll demo.odin`
+
+
+	// New vector syntax
+	u, v: [vector 3]f32;
+	v[0] = 123;
+	v.x  = 123; // valid for all vectors with count 1 to 4
+
+	// Next part
+	prefixes();
+}
+
+
+Prefix_Type :: struct {x: int, y: f32, z: rawptr};
+
+#thread_local my_tls: Prefix_Type;
+
+prefixes :: proc() {
+	using var: Prefix_Type;
+	immutable const := Prefix_Type{1, 2, nil};
+	var.x = 123;
+	x = 123;
+	// const.x = 123; // const is immutable
+
+
+
+	foo :: proc(using immutable pt: Prefix_Type, immutable int_ptr: ^int) {
+		// int_ptr = nil; // Not valid
+		// int_ptr^ = 123; // Not valid
+	}
+
+
+
+	// Same as C99's `restrict`
+	bar :: proc(no_alias a, b: ^int) {
+		// Assumes a never equals b so it can perform optimizations with that fact
+	}
+
+
+	when_statements();
+}
+
+
+
+
+
+when_statements :: proc() {
+	X :: 123 + 12;
+	Y :: X/5;
+	COND :: Y > 0;
+
+	when COND {
+		fmt.println("Y > 0");
+	} else {
+		fmt.println("Y <= 0");
+	}
+
+
+	when false {
+		this_code_does_not_exist(123, 321);
+		but_its_syntax_is_valid();
+		x :: ^^^^int;
+	}
+
+	foreign_procedures();
+}
+
+#foreign_system_library win32_user "user32.lib" when ODIN_OS == "windows";
+// NOTE: This is done on purpose for two reasons:
+// * Makes it clear where the platform specific stuff is
+// * Removes the need to solve the travelling salesman problem when importing files :P
+
+foreign_procedures :: proc() {
+	ShowWindow  :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #foreign win32_user;
+	show_window :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #foreign win32_user "ShowWindow";
+	// NOTE: If that library doesn't get used, it doesn't get linked with
+	// NOTE: There is not link checking yet to see if that procedure does come from that library
+
+	// See sys/windows.odin for more examples
+
+	special_expressions();
+}
+
+special_expressions :: proc() {
+/*
+	// Block expression
+	x := {
+		a: f32 = 123;
+		b := a-123;
+		c := b/a;
+		give c;
+	}; // semicolon is required as it's an expression
+
+	y := if x < 50 {
+		give x;
+	} else {
+		// TODO: Type cohesion is not yet finished
+		give 123;
+	}; // semicolon is required as it's an expression
+*/
+
+	// This is allows for inline blocks of code and will be a useful feature to have when
+	// macros will be implemented into the language
+
+	loops();
+}
+
+loops :: proc() {
+	// The C-style for loop
+	for i := 0; i < 123; i += 1 {
+		break;
+	}
+	for i := 0; i < 123; {
+		break;
+	}
+	for false {
+		break;
+	}
+	for {
+		break;
+	}
+
+	for i in 0..123 { // 123 exclusive
+	}
+
+	for i in 0..123-1 { // 122 inclusive
+	}
+
+	for val, idx in 12..16 {
+		fmt.println(val, idx);
+	}
+
+	primes := [..]int{2, 3, 5, 7, 11, 13, 17, 19};
+
+	for p in primes {
+		fmt.println(p);
+	}
+
+	// Pointers to arrays, slices, or strings are allowed
+	for _ in ^primes {
+		// ignore the value and just iterate across it
+	}
+
+
+
+	name := "你好,世界";
+	fmt.println(name);
+	for r in name {
+		compile_assert(type_of_val(r) == rune);
+		fmt.printf("%r\n", r);
+	}
+
+	when false {
+		for i, size := 0; i < name.count; i += size {
+			r: rune;
+			r, size = utf8.decode_rune(name[i..]);
+			fmt.printf("%r\n", r);
+		}
+	}
+
+	procedure_overloading();
+}
+
+
+procedure_overloading :: proc() {
+	THINGF :: 14451.1;
+	THINGI :: 14451;
+
+	foo :: proc() {
+		fmt.printf("Zero args\n");
+	}
+	foo :: proc(i: int) {
+		fmt.printf("int arg, i=%d\n", i);
+	}
+	foo :: proc(f: f64) {
+		i := cast(int)f;
+		fmt.printf("f64 arg, f=%d\n", i);
+	}
+
+	foo();
+	foo(THINGF);
+	// foo(THINGI); // 14451 is just a number so it could go to either procedures
+	foo(cast(int)THINGI);
+
+
+
+
+	foo :: proc(x: ^i32) -> (int, int) {
+		fmt.println("^int");
+		return 123, cast(int)(x^);
+	}
+	foo :: proc(x: rawptr) {
+		fmt.println("rawptr");
+	}
+
+
+	a: i32 = 123;
+	b: f32;
+	c: rawptr;
+	fmt.println(foo(^a));
+	foo(^b);
+	foo(c);
+	// foo(nil); // nil could go to numerous types thus the ambiguity
+
+	f: proc();
+	f = foo; // The correct `foo` to chosen
+	f();
+
+
+	// See math.odin and atomic.odin for more examples
+}

+ 318 - 0
examples/old_demos/demo006.odin

@@ -0,0 +1,318 @@
+#import "atomic.odin";
+#import "hash.odin";
+#import "mem.odin";
+#import "opengl.odin";
+#import "strconv.odin";
+#import "sync.odin";
+#import win32 "sys/windows.odin";
+
+#import "fmt.odin";
+#import "os.odin";
+#import "math.odin";
+
+
+main :: proc() {
+when true {
+/*
+	Added:
+		* Unexported entities and fields using an underscore prefix
+			- See `sync.odin` and explain
+
+	Removed:
+	 * Maybe/option types
+	 * Remove `type` keyword and other "reserved" keywords
+	 * ..< and ... removed and replace with .. (half-closed range)
+
+	Changed:
+	 * `compile_assert` and `assert` return the value of the condition for semantic reasons
+	 * thread_local -> #thread_local
+	 * #include -> #load
+	 * Files only get checked if they are actually used
+	 * match x in y {} // For type match statements
+	 * Version numbering now starts from 0.1.0 and uses the convention:
+	 	- major.minor.patch
+	 * Core library additions to Windows specific stuff
+ */
+
+	{
+		Fruit :: enum {
+			APPLE,
+			BANANA,
+			COCONUT,
+		}
+		fmt.println(Fruit.names);
+	}
+
+	{
+		A :: struct           {x, y: f32};
+		B :: struct #align 16 {x, y: f32};
+		fmt.println("align_of(A) =", align_of(A));
+		fmt.println("align_of(B) =", align_of(B));
+	}
+
+	{
+		// Removal of ..< and ...
+		for i in 0..16 {
+		}
+		// Is similar to
+		for _i := 0; _i < 16; _i++ { immutable i := _i;
+		}
+	}
+
+	{
+		thing: for i in 0..10 {
+			for j in i+1..10 {
+				if j == 2 {
+					fmt.println(i, j);
+					continue thing;
+				}
+				if j == 3 {
+					break thing;
+				}
+			}
+		}
+
+		// Works with, `for`, `for in`, `match`, `match in`
+		// NOTE(bill): This solves most of the problems I need `goto` for
+	}
+
+	{
+		t := type_info(int);
+		using Type_Info;
+		match i in t {
+		case Integer, Float:
+			fmt.println("It's a number");
+		}
+
+
+		x: any = 123;
+		foo 		match i in x {
+		case int, f32:
+			fmt.println("It's an int or f32");
+			break foo;
+		}
+	}
+
+	{
+		cond := true;
+		x: int;
+		if cond {
+			x = 3;
+		} else {
+			x = 4;
+		}
+
+
+		// Ternary operator
+		y := cond ? 3 : 4;
+
+		FOO :: true ? 123 : 432; // Constant ternary expression
+		fmt.println("Ternary values:", y, FOO);
+	}
+
+	{
+		// Slices now store a capacity
+		buf: [256]byte;
+		s: []byte;
+		s = buf[..0]; // == buf[0..0];
+		fmt.println("count =", s.count);
+		fmt.println("capacity =", s.capacity);
+		append(s, 1, 2, 3);
+		fmt.println(s);
+
+		s = buf[1..2..3];
+		fmt.println("count =", s.count);
+		fmt.println("capacity =", s.capacity);
+		fmt.println(s);
+
+		clear(s); // Sets count to zero
+		s.count = 0; // Equivalent
+	}
+
+	{
+		Foo :: struct {
+			x, y, z: f32,
+			ok:      bool,
+			flags:   u32,
+		}
+		foo_array: [256]Foo;
+		foo_as_bytes: []byte = slice_to_bytes(foo_array[..]);
+		// Useful for things like
+		// os.write(handle, foo_as_bytes);
+
+		foo_slice := slice_ptr(cast(^Foo)foo_as_bytes.data, foo_as_bytes.count/size_of(Foo), foo_as_bytes.capacity/size_of(Foo));
+		// Question: Should there be a bytes_to_slice procedure or is it clearer to do this even if it is error prone?
+		// And if so what would the syntax be?
+		// slice_transmute([]Foo, foo_as_bytes);
+	}
+
+	{
+		Vec3 :: [vector 3]f32;
+
+		x := Vec3{1, 2, 3};
+		y := Vec3{4, 5, 6};
+		fmt.println(x < y);
+		fmt.println(x + y);
+		fmt.println(x - y);
+		fmt.println(x * y);
+		fmt.println(x / y);
+
+		for i in x {
+			fmt.println(i);
+		}
+
+		compile_assert(size_of([vector 7]bool) == size_of([7]bool));
+		compile_assert(size_of([vector 7]i32) == size_of([7]i32));
+		// align_of([vector 7]i32) != align_of([7]i32) // this may be the case
+	}
+
+	{
+		// fmt.* changes
+		// bprint* returns `int` (bytes written)
+		// sprint* returns `string` (bytes written as a string)
+
+		data: [256]byte;
+		str := fmt.sprintf(data[..0], "Hellope %d %s %c", 123, "others", '!');
+		fmt.println(str);
+
+		buf := data[..0];
+		count := fmt.bprintf(^buf, "Hellope %d %s %c", 321, "y'all", '!');
+		fmt.println(cast(string)buf[..count]);
+
+		// NOTE(bill): We may change this but because this is a library feature, I am not that bothered yet
+	}
+
+	{
+		x: [dynamic]f64;
+		reserve(x, 16);
+		defer free(x); // `free` is overloaded for numerous types
+		// Number literals can have underscores in them for readability
+		append(x, 2_000_000.500_000, 123, 5, 7); // variadic append
+
+		for p, i in x {
+			if i > 0 { fmt.print(", "); }
+			fmt.print(p);
+		}
+		fmt.println();
+	}
+
+	{
+		// Dynamic array "literals"
+		x := [dynamic]f64{2_000_000.500_000, 3, 5, 7};
+		defer free(x);
+		fmt.println(x); // fmt.print* supports printing of dynamic types
+		clear(x);
+		fmt.println(x);
+	}
+
+	{
+		m: map[f32]int;
+		reserve(m, 16);
+		defer free(m);
+
+		m[1.0] = 1278;
+		m[2.0] = 7643;
+		m[3.0] = 564;
+		_, ok := m[3.0];
+		c := m[3.0];
+		assert(ok && c == 564);
+
+		fmt.print("map[");
+		i := 0;
+		for val, key in m {
+			if i > 0 {
+				fmt.print(", ");
+			}
+			fmt.printf("%v=%v", key, val);
+			i += 1;
+		}
+		fmt.println("]");
+	}
+	{
+		m := map[string]u32{
+			"a" = 56,
+			"b" = 13453,
+			"c" = 7654,
+		};
+		defer free(m);
+
+		c := m["c"];
+		_, ok := m["c"];
+		assert(ok && c == 7654);
+		fmt.println(m);
+
+		delete(m, "c"); // deletes entry with key "c"
+		_, found := m["c"];
+		assert(!found);
+
+		fmt.println(m);
+		clear(m);
+		fmt.println(m);
+
+		// NOTE: Fixed size maps are planned but we have not yet implemented
+		// them as we have had no need for them as of yet
+	}
+
+	{
+		Vector3 :: struct{x, y, z: f32};
+		Quaternion :: struct{x, y, z, w: f32};
+
+		Entity :: union {
+			// Common Fields
+			id:             u64,
+			name:           string,
+			using position: Vector3,
+			orientation:    Quaternion,
+			flags:          u32,
+
+			// Variants
+			Frog{
+				ribbit_volume: f32,
+				jump_height:   f32,
+			},
+			Door{
+				openness: f32,
+			},
+			Map{
+				width, height:   f32,
+				place_positions: []Vector3,
+				place_names:     []string,
+			},
+		}
+
+		entity: Entity;
+		// implicit conversion from variant to base type
+		entity = Entity.Frog{
+			id = 1337,
+			ribbit_volume = 0.5,
+			jump_height = 2.1,
+			/*other data */
+		};
+
+		entity.name = "Frank";
+		entity.position = Vector3{1, 4, 9};
+
+		using Entity;
+		match e in entity {
+		case Frog:
+			fmt.println("Ribbit");
+		case Door:
+			fmt.println("Creak");
+		case Map:
+			fmt.println("Rustle");
+		default:
+			fmt.println("Just a normal entity");
+		}
+
+		if frog, ok := union_cast(Frog)entity; ok {
+			fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, frog.position);
+		}
+
+		// Panics if not the correct type
+		frog: Frog;
+		frog = union_cast(Frog)entity;
+		frog, _ = union_cast(Frog)entity; // ignore error and force cast
+	}
+}
+}
+

+ 412 - 0
examples/old_demos/old_runtime.odin

@@ -0,0 +1,412 @@
+#include "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
+	compile_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()
+}

+ 430 - 0
examples/old_stuff/demo_backup.odin

@@ -0,0 +1,430 @@
+import (
+	"fmt.odin";
+	"atomics.odin";
+	"bits.odin";
+	"decimal.odin";
+	"hash.odin";
+	"math.odin";
+	"mem.odin";
+	"opengl.odin";
+	"os.odin";
+	"raw.odin";
+	"strconv.odin";
+	"strings.odin";
+	"sync.odin";
+	"sort.odin";
+	"types.odin";
+	"utf8.odin";
+	"utf16.odin";
+/*
+*/
+)
+
+
+general_stuff :: proc() {
+	// Complex numbers
+	a := 3 + 4i;
+	b: complex64 = 3 + 4i;
+	c: complex128 = 3 + 4i;
+	d := complex(2, 3);
+
+	e := a / conj(a);
+	fmt.println("(3+4i)/(3-4i) =", e);
+	fmt.println(real(e), "+", imag(e), "i");
+
+
+	// C-style variadic procedures
+	foreign __llvm_core {
+		// The variadic part allows for extra type checking too which C does not provide
+		c_printf :: proc(fmt: ^u8, #c_vararg args: ...any) -> i32 #link_name "printf" ---;
+	}
+	str := "%d\n\x00";
+	// c_printf(&str[0], i32(789456123));
+
+
+	Foo :: struct {
+		x: int;
+		y: f32;
+		z: string;
+	}
+	foo := Foo{123, 0.513, "A string"};
+	x, y, z := expand_to_tuple(foo);
+	fmt.println(x, y, z);
+	compile_assert(type_of(x) == int);
+	compile_assert(type_of(y) == f32);
+	compile_assert(type_of(z) == string);
+
+
+	// By default, all variables are zeroed
+	// This can be overridden with the "uninitialized value"
+	// This is similar to `nil` but applied to everything
+	undef_int: int = ---;
+
+
+	// Context system is now implemented using Implicit Parameter Passing (IPP)
+	// The previous implementation was Thread Local Storage (TLS)
+	// IPP has the advantage that it works on systems without TLS and that you can
+	// link the context to the stack frame and thus look at previous contexts
+	//
+	// It does mean that a pointer is implicitly passed procedures with the default
+	// Odin calling convention (#cc_odin)
+	// This can be overridden with something like #cc_contextless or #cc_c if performance
+	// is worried about
+
+}
+
+foreign_blocks :: proc() {
+	// See sys/windows.odin
+}
+
+default_arguments :: proc() {
+	hello :: proc(a: int = 9, b: int = 9) do fmt.printf("a is %d; b is %d\n", a, b);
+	fmt.println("\nTesting default arguments:");
+	hello(1, 2);
+	hello(1);
+	hello();
+}
+
+named_arguments :: proc() {
+	Colour :: enum {
+		Red,
+		Orange,
+		Yellow,
+		Green,
+		Blue,
+		Octarine,
+	};
+	using Colour;
+
+	make_character :: proc(name, catch_phrase: string, favourite_colour, least_favourite_colour: Colour) {
+		fmt.println();
+		fmt.printf("My name is %v and I like %v.  %v\n", name, favourite_colour, catch_phrase);
+	}
+
+	make_character("Frank", "¡Ay, caramba!", Blue, Green);
+
+
+	// As the procedures have more and more parameters, it is very easy
+	// to get many of the arguments in the wrong order especialy if the
+	// types are the same
+	make_character("¡Ay, caramba!", "Frank", Green, Blue);
+
+	// Named arguments help to disambiguate this problem
+	make_character(catch_phrase = "¡Ay, caramba!", name = "Frank",
+	               least_favourite_colour = Green, favourite_colour = Blue);
+
+
+	// The named arguments can be specifed in any order.
+	make_character(favourite_colour = Octarine, catch_phrase = "U wot m8!",
+	               least_favourite_colour = Green, name = "Dennis");
+
+
+	// NOTE: You cannot mix named arguments with normal values
+	/*
+	make_character("Dennis",
+	               favourite_colour = Octarine, catch_phrase = "U wot m8!",
+	               least_favourite_colour = Green);
+	*/
+
+
+	// Named arguments can also aid with default arguments
+	numerous_things :: proc(s: string, a := 1, b := 2, c := 3.14,
+	                        d := "The Best String!", e := false, f := 10.3/3.1, g := false) {
+		g_str := g ? "true" : "false";
+		fmt.printf("How many?! %s: %v\n", s, g_str);
+	}
+
+	numerous_things("First");
+	numerous_things(s = "Second", g = true);
+
+
+	// Default values can be placed anywhere, not just at the end like in other languages
+	weird :: proc(pre: string, mid: int = 0, post: string) {
+		fmt.println(pre, mid, post);
+	}
+
+	weird("How many things", 42, "huh?");
+	weird(pre = "Prefix", post = "Pat");
+
+}
+
+
+default_return_values :: proc() {
+	foo :: proc(x: int) -> (first: string = "Hellope", second := "world!") {
+		match x {
+		case 0: return;
+		case 1: return "Goodbye";
+		case 2: return "Goodbye", "cruel world...";
+		case 3: return second = "cruel world...", first = "Goodbye";
+		}
+
+		return second = "my old friend.";
+	}
+
+	fmt.printf("%s %s\n", foo(0));
+	fmt.printf("%s %s\n", foo(1));
+	fmt.printf("%s %s\n", foo(2));
+	fmt.printf("%s %s\n", foo(3));
+	fmt.printf("%s %s\n", foo(4));
+	fmt.println();
+
+
+	// A more "real" example
+	Error :: enum {
+		None,
+		WhyTheNumberThree,
+		TenIsTooBig,
+	};
+
+	Entity :: struct {
+		name: string;
+		id:   u32;
+	}
+
+	some_thing :: proc(input: int) -> (result: ^Entity = nil, err := Error.None) {
+		match {
+		case input == 3:  return err = Error.WhyTheNumberThree;
+		case input >= 10: return err = Error.TenIsTooBig;
+		}
+
+		e := new(Entity);
+		e.id = u32(input);
+
+		return result = e;
+	}
+}
+
+call_location :: proc() {
+	amazing :: proc(n: int, using loc := #caller_location) {
+		fmt.printf("%s(%d:%d) just asked to do something amazing.\n",
+				   fully_pathed_filename, line, column);
+		fmt.printf("Normal -> %d\n", n);
+		fmt.printf("Amazing -> %d\n", n+1);
+		fmt.println();
+	}
+
+	loc := #location(main);
+	fmt.println("`main` is located at", loc);
+
+	fmt.println("This line is located at", #location());
+	fmt.println();
+
+	amazing(3);
+	amazing(4, #location(call_location));
+
+	// See _preload.odin for the implementations of `assert` and `panic`
+
+}
+
+
+explicit_parametric_polymorphic_procedures :: proc() {
+	// This is how `new` is actually implemented, see _preload.odin
+	alloc_type :: proc(T: type) -> ^T do return cast(^T)alloc(size_of(T), align_of(T));
+
+	int_ptr := alloc_type(int);
+	defer free(int_ptr);
+	int_ptr^ = 137;
+	fmt.println(int_ptr, int_ptr^);
+
+	// Named arguments work too!
+	another_ptr := alloc_type(T = f32);
+	defer free(another_ptr);
+
+
+	add :: proc(T: type, args: ...T) -> T {
+		res: T;
+		for arg in args do res += arg;
+		return res;
+	}
+
+	fmt.println("add =", add(int, 1, 2, 3, 4, 5, 6));
+
+	swap :: proc(T: type, a, b: ^T) {
+		tmp := a^;
+		a^ = b^;
+		b^ = tmp;
+	}
+
+	a, b: int = 3, 4;
+	fmt.println("Pre-swap:", a, b);
+	swap(int, &a, &b);
+	fmt.println("Post-swap:", a, b);
+	a, b = b, a; // Or use this syntax for this silly example case
+
+
+	Vector2 :: struct {x, y: f32;};
+	{
+		// A more complicated example using subtyping
+		// Something like this could be used in a game
+
+		Entity :: struct {
+			using position: Vector2;
+			flags:          u64;
+			id:             u64;
+			derived:        any;
+		}
+
+		Rock :: struct {
+			using entity: Entity;
+			heavy: bool;
+		}
+		Door :: struct {
+			using entity: Entity;
+			open:         bool;
+		}
+		Monster :: struct {
+			using entity: Entity;
+			is_robot:     bool;
+			is_zombie:    bool;
+		}
+
+		new_entity :: proc(T: type, x, y: f32) -> ^T {
+			result := new(T);
+			result.derived = result^;
+			result.x = x;
+			result.y = y;
+
+			return result;
+		}
+
+		entities: [dynamic]^Entity;
+
+		rock := new_entity(Rock, 3, 5);
+
+		// Named arguments work too!
+		door := new_entity(T = Door, x = 3, y = 6);
+
+		// And named arguments can be any order
+		monster := new_entity(
+			y = 1,
+			x = 2,
+			T = Monster,
+		);
+
+		append(&entities, rock, door, monster);
+
+		fmt.println("Subtyping");
+		for entity in entities {
+			match e in entity.derived {
+			case Rock:    fmt.println("Rock",    e.x, e.y);
+			case Door:    fmt.println("Door",    e.x, e.y);
+			case Monster: fmt.println("Monster", e.x, e.y);
+			}
+		}
+	}
+	{
+		Entity :: struct {
+			using position: Vector2;
+			flags:          u64;
+			id:             u64;
+			variant: union { Rock, Door, Monster };
+		}
+
+		Rock :: struct {
+			using entity: ^Entity;
+			heavy: bool;
+		}
+		Door :: struct {
+			using entity: ^Entity;
+			open:         bool;
+		}
+		Monster :: struct {
+			using entity: ^Entity;
+			is_robot:     bool;
+			is_zombie:    bool;
+		}
+
+		new_entity :: proc(T: type, x, y: f32) -> ^T {
+			result := new(Entity);
+			result.variant = T{entity = result};
+			result.x = x;
+			result.y = y;
+
+			return cast(^T)&result.variant;
+		}
+
+		entities: [dynamic]^Entity;
+
+		rock := new_entity(Rock, 3, 5);
+
+		// Named arguments work too!
+		door := new_entity(T = Door, x = 3, y = 6);
+
+		// And named arguments can be any order
+		monster := new_entity(
+			y = 1,
+			x = 2,
+			T = Monster,
+		);
+
+		append(&entities, rock, door, monster);
+
+		fmt.println("Union");
+		for entity in entities {
+			match e in entity.variant {
+			case Rock:    fmt.println("Rock",    e.x, e.y);
+			case Door:    fmt.println("Door",    e.x, e.y);
+			case Monster: fmt.println("Monster", e.x, e.y);
+			}
+		}
+	}
+}
+
+
+implicit_polymorphic_assignment :: proc() {
+	yep :: proc(p: proc(x: int)) {
+		p(123);
+	}
+
+	frank :: proc(x: $T)    do fmt.println("frank ->", x);
+	tim   :: proc(x, y: $T) do fmt.println("tim ->", x, y);
+	yep(frank);
+	// yep(tim);
+}
+
+
+
+
+main :: proc() {
+/*
+	foo :: proc(x: i64,  y: f32) do fmt.println("#1", x, y);
+	foo :: proc(x: type, y: f32) do fmt.println("#2", type_info(x), y);
+	foo :: proc(x: type)         do fmt.println("#3", type_info(x));
+
+	f :: foo;
+
+	f(y = 3785.1546, x = 123);
+	f(x = int, y = 897.513);
+	f(x = f32);
+
+	general_stuff();
+	foreign_blocks();
+	default_arguments();
+	named_arguments();
+	default_return_values();
+	call_location();
+	explicit_parametric_polymorphic_procedures();
+	implicit_polymorphic_assignment();
+
+
+	// Command line argument(s)!
+	// -opt=0,1,2,3
+*/
+/*
+	program := "+ + * - /";
+	accumulator := 0;
+
+	for token in program {
+		match token {
+		case '+': accumulator += 1;
+		case '-': accumulator -= 1;
+		case '*': accumulator *= 2;
+		case '/': accumulator /= 2;
+		case: // Ignore everything else
+		}
+	}
+
+	fmt.printf("The program \"%s\" calculates the value %d\n",
+			   program, accumulator);
+*/
+}

+ 479 - 0
examples/punity.odin

@@ -0,0 +1,479 @@
+import win32 "sys/windows.odin";
+import "fmt.odin";
+import "os.odin";
+import "mem.odin";
+
+
+CANVAS_WIDTH  :: 128;
+CANVAS_HEIGHT :: 128;
+CANVAS_SCALE  :: 3;
+FRAME_TIME    :: 1.0/30.0;
+WINDOW_TITLE  :: "Punity\x00";
+
+_ :: compile_assert(CANVAS_WIDTH % 16 == 0);
+
+
+WINDOW_WIDTH  :: CANVAS_WIDTH  * CANVAS_SCALE;
+WINDOW_HEIGHT :: CANVAS_HEIGHT * CANVAS_SCALE;
+
+
+STACK_CAPACITY   :: 1<<20;
+STORAGE_CAPACITY :: 1<<20;
+
+DRAW_LIST_RESERVE :: 128;
+
+MAX_KEYS :: 256;
+
+Core :: struct {
+	stack:   ^Bank;
+	storage: ^Bank;
+
+	running:       bool;
+	key_modifiers: u32;
+	key_states:    [MAX_KEYS]u8;
+	key_deltas:    [MAX_KEYS]u8;
+
+	perf_frame,
+	perf_frame_inner,
+	perf_step,
+	perf_audio,
+	perf_blit,
+	perf_blit_cvt,
+	perf_blit_gdi: Perf_Span;
+
+	frame: i64;
+
+	canvas:    Canvas;
+	draw_list: ^Draw_List;
+}
+
+Perf_Span :: struct {
+	stamp: f64;
+	delta: f32;
+}
+
+Bank :: struct {
+	memory: []u8;
+	cursor: int;
+}
+
+Bank_State :: struct {
+	state: Bank;
+	bank: ^Bank;
+}
+
+
+Color :: struct #raw_union {
+	using channels: struct{a, b, g, r: u8};
+	rgba: u32;
+}
+
+Palette :: struct {
+	colors: [256]Color;
+	colors_count: u8;
+}
+
+
+Rect :: struct #raw_union {
+	using minmax: struct {min_x, min_y, max_x, max_y: int};
+	using pos: struct {left, top, right, bottom: int};
+	e: [4]int;
+}
+
+Bitmap :: struct {
+	pixels: []u8;
+	width:  int;
+	height: int;
+}
+
+Font :: struct {
+	using bitmap: Bitmap;
+	char_width:   int;
+	char_height:  int;
+}
+
+Canvas :: struct {
+	using bitmap: ^Bitmap;
+	palette:      Palette;
+	translate_x:  int;
+	translate_y:  int;
+	clip:         Rect;
+	font:         ^Font;
+}
+
+DrawFlag :: enum {
+	NONE   = 0,
+	FLIP_H = 1<<0,
+	FLIP_V = 1<<1,
+	MASK   = 1<<2,
+}
+
+Draw_Item :: struct {}
+Draw_List :: struct {
+	items: []Draw_Item;
+}
+
+Key :: enum {
+	Mod_Shift   = 0x0001,
+	Mod_Control = 0x0002,
+	Mod_Alt     = 0x0004,
+	Mod_Super   = 0x0008,
+
+
+	Unknown            =-1,
+	Invalid            =-2,
+
+
+	Lbutton            = 1,
+	Rbutton            = 2,
+	Cancel             = 3,
+	Mbutton            = 4,
+
+
+	Back               = 8,
+	Tab                = 9,
+	Clear              = 12,
+	Return             = 13,
+	Shift              = 16,
+	Control            = 17,
+	Menu               = 18,
+	Pause              = 19,
+	Capital            = 20,
+	Kana               = 0x15,
+	Hangeul            = 0x15,
+	Hangul             = 0x15,
+	Junja              = 0x17,
+	Final              = 0x18,
+	Hanja              = 0x19,
+	Kanji              = 0x19,
+	Escape             = 0x1B,
+	Convert            = 0x1C,
+	Non_Convert        = 0x1D,
+	Accept             = 0x1E,
+	Mode_Change        = 0x1F,
+	Space              = 32,
+	Prior              = 33,
+	Next               = 34,
+	End                = 35,
+	Home               = 36,
+	Left               = 37,
+	Up                 = 38,
+	Right              = 39,
+	Down               = 40,
+	Select             = 41,
+	Print              = 42,
+	Exec               = 43,
+	Snapshot           = 44,
+	Insert             = 45,
+	Delete             = 46,
+	Help               = 47,
+	Lwin               = 0x5B,
+	Rwin               = 0x5C,
+	Apps               = 0x5D,
+	Sleep              = 0x5F,
+	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,
+
+
+	Apostrophe         = 39,  /* ' */
+	Comma              = 44,  /* , */
+	Minus              = 45,  /* - */
+	Period             = 46,  /* . */
+	Slash              = 47,  /* / */
+	Num0               = 48,
+	Num1               = 49,
+	Num2               = 50,
+	Num3               = 51,
+	Num4               = 52,
+	Num5               = 53,
+	Num6               = 54,
+	Num7               = 55,
+	Num8               = 56,
+	Num9               = 57,
+	Semicolon          = 59,  /* ; */
+	Equal              = 61,  /* = */
+	A                  = 65,
+	B                  = 66,
+	C                  = 67,
+	D                  = 68,
+	E                  = 69,
+	F                  = 70,
+	G                  = 71,
+	H                  = 72,
+	I                  = 73,
+	J                  = 74,
+	K                  = 75,
+	L                  = 76,
+	M                  = 77,
+	N                  = 78,
+	O                  = 79,
+	P                  = 80,
+	Q                  = 81,
+	R                  = 82,
+	S                  = 83,
+	T                  = 84,
+	U                  = 85,
+	V                  = 86,
+	W                  = 87,
+	X                  = 88,
+	Y                  = 89,
+	Z                  = 90,
+	Left_Bracket       = 91,  /* [ */
+	Backslash          = 92,  /* \ */
+	Right_Bracket      = 93,  /* ] */
+	Grave_Accent       = 96,  /* ` */
+};
+
+
+key_down :: proc(k: Key) -> bool {
+	return _core.key_states[k] != 0;
+}
+
+key_pressed :: proc(k: Key) -> bool {
+	return (_core.key_deltas[k] != 0) && key_down(k);
+}
+
+
+
+
+win32_perf_count_freq := win32.get_query_performance_frequency();
+time_now :: proc() -> f64 {
+	assert(win32_perf_count_freq != 0);
+
+	counter: i64;
+	win32.query_performance_counter(&counter);
+	return f64(counter) / f64(win32_perf_count_freq);
+}
+
+_core: Core;
+
+run :: proc(user_init, user_step: proc(c: ^Core)) {
+	using win32;
+
+	_core.running = true;
+
+	win32_proc :: proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline #cc_c {
+		win32_app_key_mods :: proc() -> u32 {
+			mods: u32 = 0;
+
+			if is_key_down(Key_Code.Shift)   do mods |= u32(Key.Mod_Shift);
+			if is_key_down(Key_Code.Control) do mods |= u32(Key.Mod_Control);
+			if is_key_down(Key_Code.Menu)    do mods |= u32(Key.Mod_Alt);
+			if is_key_down(Key_Code.Lwin)    do mods |= u32(Key.Mod_Super);
+			if is_key_down(Key_Code.Rwin)    do mods |= u32(Key.Mod_Super);
+
+			return mods;
+		}
+
+		match msg {
+		case WM_KEYDOWN:
+			_core.key_modifiers = win32_app_key_mods();
+			if wparam < MAX_KEYS {
+				_core.key_states[wparam] = 1;
+				_core.key_deltas[wparam] = 1;
+			}
+			return 0;
+
+		case WM_KEYUP:
+			_core.key_modifiers = win32_app_key_mods();
+			if wparam < MAX_KEYS {
+				_core.key_states[wparam] = 0;
+				_core.key_deltas[wparam] = 1;
+			}
+			return 0;
+
+		case WM_CLOSE:
+			post_quit_message(0);
+			_core.running = false;
+			return 0;
+		}
+
+		return def_window_proc_a(hwnd, msg, wparam, lparam);
+	}
+
+
+	class_name := "Punity\x00";
+	window_class := Wnd_Class_Ex_A{
+		class_name = &class_name[0],
+		size       = size_of(Wnd_Class_Ex_A),
+		style      = CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
+		instance   = Hinstance(get_module_handle_a(nil)),
+		wnd_proc   = win32_proc,
+		background = Hbrush(get_stock_object(BLACK_BRUSH)),
+	};
+
+	if register_class_ex_a(&window_class) == 0 {
+		fmt.fprintln(os.stderr, "register_class_ex_a failed");
+		return;
+	}
+
+	screen_width  := get_system_metrics(SM_CXSCREEN);
+	screen_height := get_system_metrics(SM_CYSCREEN);
+
+	rc: Rect;
+	rc.left   = (screen_width - WINDOW_WIDTH)   / 2;
+	rc.top    = (screen_height - WINDOW_HEIGHT) / 2;
+	rc.right  = rc.left + WINDOW_WIDTH;
+	rc.bottom = rc.top + WINDOW_HEIGHT;
+
+	style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
+	assert(adjust_window_rect(&rc, style, 0) != 0);
+
+	wt := WINDOW_TITLE;
+
+	win32_window := create_window_ex_a(0,
+	                                   window_class.class_name,
+	                                   &wt[0],
+	                                   style,
+	                                   rc.left, rc.top,
+	                                   rc.right-rc.left, rc.bottom-rc.top,
+	                                   nil, nil, window_class.instance,
+	                                   nil);
+
+	if win32_window == nil {
+		fmt.fprintln(os.stderr, "create_window_ex_a failed");
+		return;
+	}
+
+
+	window_bmi: Bitmap_Info;
+	window_bmi.size        = size_of(Bitmap_Info_Header);
+	window_bmi.width       = CANVAS_WIDTH;
+	window_bmi.height      = CANVAS_HEIGHT;
+	window_bmi.planes      = 1;
+	window_bmi.bit_count   = 32;
+	window_bmi.compression = BI_RGB;
+
+
+	user_init(&_core);
+
+	show_window(win32_window, SW_SHOW);
+
+	window_buffer := make([]u32, CANVAS_WIDTH * CANVAS_HEIGHT);
+	defer free(window_buffer);
+
+	for _, i in window_buffer do window_buffer[i] = 0xff00ff;
+
+	dt: f64;
+	prev_time := time_now();
+	curr_time := time_now();
+	total_time: f64 = 0;
+	offset_x := 0;
+	offset_y := 0;
+
+	message: Msg;
+	for _core.running {
+		curr_time = time_now();
+		dt = curr_time - prev_time;
+		prev_time = curr_time;
+		total_time += dt;
+
+		offset_x += 1;
+		offset_y += 2;
+
+		{
+			buf: [128]u8;
+			s := fmt.bprintf(buf[..], "Punity: %.4f ms\x00", dt*1000);
+			win32.set_window_text_a(win32_window, &s[0]);
+		}
+
+
+		for y in 0..CANVAS_HEIGHT {
+			for x in 0..CANVAS_WIDTH {
+				g := (x % 32) * 8;
+				b := (y % 32) * 8;
+				window_buffer[x + y*CANVAS_WIDTH] = u32(g << 8 | b);
+			}
+		}
+
+		mem.zero(&_core.key_deltas[0], size_of(_core.key_deltas));
+
+		for peek_message_a(&message, nil, 0, 0, PM_REMOVE) != 0 {
+			if message.message == WM_QUIT {
+				_core.running = false;
+			}
+			translate_message(&message);
+			dispatch_message_a(&message);
+		}
+
+		user_step(&_core);
+
+		dc := get_dc(win32_window);
+		stretch_dibits(dc,
+		               0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE,
+		               0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
+		               &window_buffer[0],
+		               &window_bmi,
+		               DIB_RGB_COLORS,
+		               SRCCOPY);
+		release_dc(win32_window, dc);
+
+
+
+		delta := time_now() - prev_time;
+		if ms := i32((FRAME_TIME - delta) * 1000); ms > 0 {
+			win32.sleep(ms);
+		}
+
+		_core.frame += 1;
+	}
+}
+
+
+main :: proc() {
+	user_init :: proc(c: ^Core) {
+
+	}
+
+	user_step :: proc(c: ^Core) {
+
+	}
+
+	run(user_init, user_step);
+}

+ 23 - 21
src/checker.cpp

@@ -2251,9 +2251,8 @@ Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c, Map<Scope
 		AstNode *decl = c->delayed_imports[i].decl;
 		GB_ASSERT(parent->is_file);
 
-		if (decl->kind == AstNode_ImportDecl) {
-			ast_node(id, ImportDecl, decl);
-
+		switch (decl->kind) {
+		case_ast_node(id, ImportDecl, decl);
 			String path = id->fullpath;
 			HashKey key = hash_string(path);
 			Scope **found = map_get(file_scopes, key);
@@ -2287,9 +2286,9 @@ Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c, Map<Scope
 				import_graph_node_set_add(&m->succ, n);
 				ptr_set_add(&m->scope->imported, n->scope);
 			}
-		} else if (decl->kind == AstNode_ExportDecl) {
-			ast_node(ed, ExportDecl, decl);
+		case_end;
 
+		case_ast_node(ed, ExportDecl, decl);
 			String path = ed->fullpath;
 			HashKey key = hash_string(path);
 			Scope **found = map_get(file_scopes, key);
@@ -2321,6 +2320,7 @@ Array<ImportGraphNode *> generate_import_dependency_graph(Checker *c, Map<Scope
 			import_graph_node_set_add(&n->pred, m);
 			import_graph_node_set_add(&m->succ, n);
 			ptr_set_add(&m->scope->imported, n->scope);
+		case_end;
 		}
 	}
 
@@ -2461,8 +2461,8 @@ void check_import_entities(Checker *c, Map<Scope *> *file_scopes) {
 		for_array(i, node->decls) {
 			AstNode *decl = node->decls[i];
 
-			if (decl->kind == AstNode_ImportDecl) {
-				ast_node(id, ImportDecl, decl);
+			switch (decl->kind) {
+			case_ast_node(id, ImportDecl, decl);
 				Token token = id->relpath;
 
 				GB_ASSERT(parent_scope->is_file);
@@ -2497,8 +2497,10 @@ void check_import_entities(Checker *c, Map<Scope *> *file_scopes) {
 					}
 				}
 
-				if (ptr_set_add(&parent_scope->imported, scope)) {
-					// warning(token, "Multiple import of the same file within this scope");
+				if (ptr_set_exists(&parent_scope->imported, scope)) {
+					// error(token, "Multiple import of the same file within this scope");
+				} else {
+					ptr_set_add(&parent_scope->imported, scope);
 				}
 
 				scope->has_been_imported = true;
@@ -2515,14 +2517,10 @@ void check_import_entities(Checker *c, Map<Scope *> *file_scopes) {
 							if (!is_entity_kind_exported(e->kind)) {
 								continue;
 							}
-							if (id->import_name.string == ".") {
-								add_entity(c, parent_scope, e->identifier, e);
-							} else {
-								if (is_entity_exported(e)) {
-									// TODO(bill): Should these entities be imported but cause an error when used?
-									bool ok = add_entity(c, parent_scope, e->identifier, e);
-									if (ok) map_set(&parent_scope->implicit, hash_entity(e), true);
-								}
+							if (is_entity_exported(e)) {
+								// TODO(bill): Should these entities be imported but cause an error when used?
+								bool ok = add_entity(c, parent_scope, e->identifier, e);
+								if (ok) map_set(&parent_scope->implicit, hash_entity(e), true);
 							}
 						}
 					}
@@ -2540,8 +2538,9 @@ void check_import_entities(Checker *c, Map<Scope *> *file_scopes) {
 						add_entity(c, parent_scope, nullptr, e);
 					}
 				}
-			} else if (decl->kind == AstNode_ExportDecl) {
-				ast_node(ed, ExportDecl, decl);
+			case_end;
+
+			case_ast_node(ed, ExportDecl, decl);
 				Token token = ed->relpath;
 
 				GB_ASSERT(parent_scope->is_file);
@@ -2576,8 +2575,10 @@ void check_import_entities(Checker *c, Map<Scope *> *file_scopes) {
 					}
 				}
 
-				if (ptr_set_add(&parent_scope->imported, scope)) {
-					// warning(token, "Multiple import of the same file within this scope");
+				if (ptr_set_exists(&parent_scope->imported, scope)) {
+					// error(token, "Multiple import of the same file within this scope");
+				} else {
+					ptr_set_add(&parent_scope->imported, scope);
 				}
 
 				scope->has_been_imported = true;
@@ -2594,6 +2595,7 @@ void check_import_entities(Checker *c, Map<Scope *> *file_scopes) {
 						}
 					}
 				}
+			case_end;
 			}
 		}
 	}

+ 2 - 6
src/ptr_set.cpp

@@ -19,7 +19,7 @@ struct PtrSet {
 
 template <typename T> void ptr_set_init             (PtrSet<T> *s, gbAllocator a, isize capacity = 16);
 template <typename T> void ptr_set_destroy          (PtrSet<T> *s);
-template <typename T> bool ptr_set_add              (PtrSet<T> *s, T ptr);
+template <typename T> void ptr_set_add              (PtrSet<T> *s, T ptr);
 template <typename T> bool ptr_set_exists           (PtrSet<T> *s, T ptr);
 template <typename T> void ptr_set_remove           (PtrSet<T> *s, T ptr);
 template <typename T> void ptr_set_clear            (PtrSet<T> *s);
@@ -136,8 +136,7 @@ gb_inline bool ptr_set_exists(PtrSet<T> *s, T ptr) {
 
 // Returns true if it already exists
 template <typename T>
-bool ptr_set_add(PtrSet<T> *s, T ptr) {
-	bool exists = false;
+void ptr_set_add(PtrSet<T> *s, T ptr) {
 	isize index;
 	PtrSetFindResult fr;
 	if (s->hashes.count == 0) {
@@ -146,7 +145,6 @@ bool ptr_set_add(PtrSet<T> *s, T ptr) {
 	fr = ptr_set__find(s, ptr);
 	if (fr.entry_index >= 0) {
 		index = fr.entry_index;
-		exists = true;
 	} else {
 		index = ptr_set__add_entry(s, ptr);
 		if (fr.entry_prev >= 0) {
@@ -158,8 +156,6 @@ bool ptr_set_add(PtrSet<T> *s, T ptr) {
 	if (ptr_set__full(s)) {
 		ptr_set_grow(s);
 	}
-
-	return exists;
 }