diff --git a/examples/demo.odin b/examples/demo.odin
new file mode 100644
index 000000000..de0dce69c
--- /dev/null
+++ b/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();
+ }
+}
+
diff --git a/examples/factorial.odin b/examples/factorial.odin
new file mode 100644
index 000000000..af39bbac1
--- /dev/null
+++ b/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));
+}
diff --git a/examples/game.odin b/examples/game.odin
new file mode 100644
index 000000000..1f09e9209
--- /dev/null
+++ b/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();
+}
diff --git a/examples/hello.odin b/examples/hello.odin
new file mode 100644
index 000000000..96b7af0fa
--- /dev/null
+++ b/examples/hello.odin
@@ -0,0 +1,5 @@
+import "fmt.odin";
+
+main :: proc() {
+ fmt.println("Hellope, world!");
+}
diff --git a/examples/http_test.odin b/examples/http_test.odin
new file mode 100644
index 000000000..f3359b1b8
--- /dev/null
+++ b/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
+
+
+
+ Demo Title
+
+
+ Odin Server Demo
+
+
+`;
+
+ var buf: [1024]u8;
+ for {
+ var bytes = recv(client, &buf[0], i32(len(buf)), 0);
+ if bytes > 0 {
+ // fmt.println(string(buf[0.. 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;
+ }
+
+}
diff --git a/examples/old_demos/demo001.odin b/examples/old_demos/demo001.odin
new file mode 100644
index 000000000..5f30af8fe
--- /dev/null
+++ b/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")
+}
+
+
+
+
diff --git a/examples/old_demos/demo002.odin b/examples/old_demos/demo002.odin
new file mode 100644
index 000000000..78cdb194a
--- /dev/null
+++ b/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(); }
diff --git a/examples/old_demos/demo004.odin b/examples/old_demos/demo004.odin
new file mode 100644
index 000000000..86588d60d
--- /dev/null
+++ b/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
+}
diff --git a/examples/old_demos/demo005.odin b/examples/old_demos/demo005.odin
new file mode 100644
index 000000000..f9d1a4a4c
--- /dev/null
+++ b/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
+}
diff --git a/examples/old_demos/demo006.odin b/examples/old_demos/demo006.odin
new file mode 100644
index 000000000..2f4ffef66
--- /dev/null
+++ b/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
+ }
+}
+}
+
diff --git a/examples/old_demos/old_runtime.odin b/examples/old_demos/old_runtime.odin
new file mode 100644
index 000000000..655058e0a
--- /dev/null
+++ b/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()
+}
diff --git a/examples/old_stuff/demo_backup.odin b/examples/old_stuff/demo_backup.odin
new file mode 100644
index 000000000..8eeaeb357
--- /dev/null
+++ b/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);
+*/
+}
diff --git a/examples/punity.odin b/examples/punity.odin
new file mode 100644
index 000000000..33b1aecdf
--- /dev/null
+++ b/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);
+}
diff --git a/src/checker.cpp b/src/checker.cpp
index 332a4ac19..2c90b9e80 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -2251,9 +2251,8 @@ Array generate_import_dependency_graph(Checker *c, Mapdelayed_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 generate_import_dependency_graph(Checker *c, Mapsucc, 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 generate_import_dependency_graph(Checker *c, Mappred, 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 *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 *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 *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 *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 *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 *file_scopes) {
}
}
}
+ case_end;
}
}
}
diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp
index 7f1c12c17..d87a86811 100644
--- a/src/ptr_set.cpp
+++ b/src/ptr_set.cpp
@@ -19,7 +19,7 @@ struct PtrSet {
template void ptr_set_init (PtrSet *s, gbAllocator a, isize capacity = 16);
template void ptr_set_destroy (PtrSet *s);
-template bool ptr_set_add (PtrSet *s, T ptr);
+template void ptr_set_add (PtrSet *s, T ptr);
template bool ptr_set_exists (PtrSet *s, T ptr);
template void ptr_set_remove (PtrSet *s, T ptr);
template void ptr_set_clear (PtrSet *s);
@@ -136,8 +136,7 @@ gb_inline bool ptr_set_exists(PtrSet *s, T ptr) {
// Returns true if it already exists
template
-bool ptr_set_add(PtrSet *s, T ptr) {
- bool exists = false;
+void ptr_set_add(PtrSet *s, T ptr) {
isize index;
PtrSetFindResult fr;
if (s->hashes.count == 0) {
@@ -146,7 +145,6 @@ bool ptr_set_add(PtrSet *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 *s, T ptr) {
if (ptr_set__full(s)) {
ptr_set_grow(s);
}
-
- return exists;
}