From c60fb10a6a0b668e9dab7e4cb899664f5e32419e Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sat, 26 Oct 2019 10:40:17 +0100 Subject: Move old demos and old stuff to /misc --- examples/old_demos/demo001.odin | 337 -------------- examples/old_demos/demo002.odin | 879 ------------------------------------ examples/old_demos/demo004.odin | 66 --- examples/old_demos/demo005.odin | 283 ------------ examples/old_demos/demo006.odin | 310 ------------- examples/old_demos/demo007.odin | 570 ----------------------- examples/old_demos/demo008.odin | 778 ------------------------------- examples/old_demos/old_runtime.odin | 412 ----------------- examples/old_stuff/demo_backup.odin | 430 ------------------ misc/old_demos/demo001.odin | 337 ++++++++++++++ misc/old_demos/demo002.odin | 879 ++++++++++++++++++++++++++++++++++++ misc/old_demos/demo004.odin | 66 +++ misc/old_demos/demo005.odin | 283 ++++++++++++ misc/old_demos/demo006.odin | 310 +++++++++++++ misc/old_demos/demo007.odin | 570 +++++++++++++++++++++++ misc/old_demos/demo008.odin | 778 +++++++++++++++++++++++++++++++ misc/old_demos/old_runtime.odin | 412 +++++++++++++++++ misc/old_stuff/demo_backup.odin | 430 ++++++++++++++++++ misc/shell.bat | 4 +- 19 files changed, 4067 insertions(+), 4067 deletions(-) delete mode 100644 examples/old_demos/demo001.odin delete mode 100644 examples/old_demos/demo002.odin delete mode 100644 examples/old_demos/demo004.odin delete mode 100644 examples/old_demos/demo005.odin delete mode 100644 examples/old_demos/demo006.odin delete mode 100644 examples/old_demos/demo007.odin delete mode 100644 examples/old_demos/demo008.odin delete mode 100644 examples/old_demos/old_runtime.odin delete mode 100644 examples/old_stuff/demo_backup.odin create mode 100644 misc/old_demos/demo001.odin create mode 100644 misc/old_demos/demo002.odin create mode 100644 misc/old_demos/demo004.odin create mode 100644 misc/old_demos/demo005.odin create mode 100644 misc/old_demos/demo006.odin create mode 100644 misc/old_demos/demo007.odin create mode 100644 misc/old_demos/demo008.odin create mode 100644 misc/old_demos/old_runtime.odin create mode 100644 misc/old_stuff/demo_backup.odin diff --git a/examples/old_demos/demo001.odin b/examples/old_demos/demo001.odin deleted file mode 100644 index a3aea1cb7..000000000 --- a/examples/old_demos/demo001.odin +++ /dev/null @@ -1,337 +0,0 @@ -import "core:fmt.odin"; -import "core:os.odin"; -import "core: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_of(int); // by type - info = type_info_of(x); // by value - // See: runtime.odin - - match i in info.variant { - case Type_Info_Integer: - fmt.println("integer!"); - case Type_Info_Float: - fmt.println("float!"); - case: - 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(v1); - t2 := type_info_of(v2); - t3 := type_info_of(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_of(Fruit); - name := fruit_ti.variant.(Type_Info_Named).name; - info, _ := type_info_base(fruit_ti).variant.(Type_Info_Enum); - - fmt.printf("%s :: enum %T {\n", name, info.base); - for _, i in info.values { - 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) - // #assert(false) - // panic("Panic message goes here") -} - - - - diff --git a/examples/old_demos/demo002.odin b/examples/old_demos/demo002.odin deleted file mode 100644 index a790aadf3..000000000 --- a/examples/old_demos/demo002.odin +++ /dev/null @@ -1,879 +0,0 @@ -// Demo 002 -export "core:fmt.odin"; -export "core:math.odin"; -export "core:mem.odin"; -// export "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 do 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 do 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? - - push_allocator default_allocator() { - a := new(int); - defer free(a); - - // Do whatever - - } - } - - { - a: int = 123; - b: type_of(a) = 321; - - // NOTE(bill): This matches the current naming scheme - // size_of - // align_of - // offset_of - // - // size_of_val - // align_of_val - // offset_of_val - // type_of_val - } - - { - // Compile time assert - COND :: true; - #assert(COND); - // #assert(!COND) - - // Runtime assert - x := true; - assert(x); - // assert(!x); - } - - { - x: ^u32 = 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 - - case: - print("default!\n"); - } - - - - match x := 1.5; x { - case 1.5: - print("1.5!\n"); - // break by default - case TAU: - print("τ!\n"); - case: - print("default!\n"); - } - - - - match x := "Hello"; x { - case "Hello": - print("greeting\n"); - // break by default - case "Goodbye": - print("farewell\n"); - case: - 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"); - case: - 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 do 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^ += 1; - 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() { - { - Entity_Kind :: enum { - INVALID, - FROG, - GIRAFFE, - HELICOPTER, - } - - Entity :: struct { - kind: Entity_Kind - using data: struct #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 = Entity_Kind.FROG; - e.frog.jump_height = 12; - - f: type_of(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 - } - - { - 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, - } - Entity :: union {Frog, Giraffe, Helicopter}; - - 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"); - case: - 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 {}; - - Entity_Kind :: enum { - Invalid, - Constant, - Variable, - Using_Variable, - TypeName, - Procedure, - Builtin, - Count, - } - - Guid :: i64; - Entity :: struct { - - kind: Entity_Kind, - guid: Guid, - - scope: ^Scope, - token: Token, - type_: ^Type, - - using data: struct #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 - }, - Using_Variable: 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 { - } - - - 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 - } - Using_Variable :: struct { - } - TypeName :: struct { - } - Procedure :: struct { - used: bool, - } - Builtin :: struct { - id: int, - } - - Entity :: struct { - guid: Guid, - - scope: ^Scope, - token: Token, - type_: ^Type, - - variant: union {Constant, Variable, Using_Variable, TypeName, Procedure, Builtin}, - } - - e := Entity{ - variant = 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 :: struct #raw_union { - using xy_: struct { x, y: f32 }, - e: [2]f32, - v: [vector 2]f32, - } - - Vector3 :: struct #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 deleted file mode 100644 index c9acc9a15..000000000 --- a/examples/old_demos/demo004.odin +++ /dev/null @@ -1,66 +0,0 @@ -import "core:fmt.odin"; -import "core:utf8.odin"; -import "core:hash.odin"; -import "core:mem.odin"; - -main :: proc() { - { // New Standard Library stuff - s := "Hello"; - fmt.println(s, - utf8.valid_string(s), - hash.murmur64(cast([]u8)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.destroy_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 deleted file mode 100644 index c8273b03b..000000000 --- a/examples/old_demos/demo005.odin +++ /dev/null @@ -1,283 +0,0 @@ -import "core:fmt.odin"; -import "core:utf8.odin"; -// import "core:atomic.odin"; -// import "core:hash.odin"; -// import "core:math.odin"; -// import "core:mem.odin"; -// import "core:opengl.odin"; -// import "core:os.odin"; -// import "core:sync.odin"; -// import win32 "core: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; #assert(size_of(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; - var.x = 123; - x = 123; - - - foo :: proc(using pt: Prefix_Type) { - } - - - - // 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(); -} - -when ODIN_OS == "windows" { - foreign_system_library win32_user "user32.lib"; -} -// 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() { - foreign win32_user { - ShowWindow :: proc(hwnd: rawptr, cmd_show: i32) -> i32 ---; - show_window :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #link_name "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 { - #assert(type_of(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 deleted file mode 100644 index c2f64151b..000000000 --- a/examples/old_demos/demo006.odin +++ /dev/null @@ -1,310 +0,0 @@ -// import "core:atomic.odin"; -import "core:hash.odin"; -import "core:mem.odin"; -import "core:opengl.odin"; -import "core:strconv.odin"; -import "core:sync.odin"; -import win32 "core:sys/windows.odin"; - -import "core:fmt.odin"; -import "core:os.odin"; -import "core: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: - * `#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 += 1 { - } - } - - { - 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_of(int); - match i in t.variant { - case Type_Info_Integer, Type_Info_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]u8; - s: []u8; - s = buf[..0]; // == buf[0..0]; - fmt.println("count =", len(s)); - fmt.println("capacity =", cap(s)); - append(&s, 1, 2, 3); - fmt.println(s); - - s = buf[1..2..3]; - fmt.println("count =", len(s)); - fmt.println("capacity =", cap(s)); - fmt.println(s); - - clear(&s); // Sets count to zero - } - - { - Foo :: struct { - x, y, z: f32, - ok: bool, - flags: u32, - } - foo_array: [256]Foo; - foo_as_bytes: []u8 = mem.slice_to_bytes(foo_array[..]); - // Useful for things like - // os.write(handle, foo_as_bytes); - - foo_slice := mem.slice_ptr(cast(^Foo)&foo_as_bytes[0], len(foo_as_bytes)/size_of(Foo), cap(foo_as_bytes)/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); - } - - #assert(size_of([vector 7]bool) >= size_of([7]bool)); - #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 `string` - - data: [256]u8; - str := fmt.bprintf(data[..], "Hellope %d %s %c", 123, "others", '!'); - fmt.println(str); - } - - { - 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}; - - // Variants - Frog :: struct { - ribbit_volume: f32, - jump_height: f32, - } - Door :: struct { - openness: f32, - } - Map :: struct { - width, height: f32, - place_positions: []Vector3, - place_names: []string, - } - - Entity :: struct { - // Common Fields - id: u64, - name: string, - using position: Vector3, - orientation: Quaternion, - flags: u32, - - variant: union { Frog, Door, Map }, - } - - entity: Entity; - entity.id = 1337; - // implicit conversion from variant to base type - entity.variant = Frog{ - ribbit_volume = 0.5, - jump_height = 2.1, - /*other data */ - }; - - entity.name = "Frank"; - entity.position = Vector3{1, 4, 9}; - - match e in entity.variant { - case Frog: - fmt.println("Ribbit"); - case Door: - fmt.println("Creak"); - case Map: - fmt.println("Rustle"); - case: - fmt.println("Just a normal entity"); - } - - if frog, ok := entity.variant.(Frog); ok { - fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, entity.position); - } - - // Panics if not the correct type - frog: Frog; - frog = entity.variant.(Frog); - frog, _ = entity.variant.(Frog); // ignore error and force cast - } -} -} - diff --git a/examples/old_demos/demo007.odin b/examples/old_demos/demo007.odin deleted file mode 100644 index d19446ecb..000000000 --- a/examples/old_demos/demo007.odin +++ /dev/null @@ -1,570 +0,0 @@ -import "core:fmt.odin" -import "core:strconv.odin" -import "core:mem.odin" -import "core:bits.odin" -import "core:hash.odin" -import "core:math.odin" -import "core:os.odin" -import "core:raw.odin" -import "core:sort.odin" -import "core:strings.odin" -import "core:types.odin" -import "core:utf16.odin" -import "core:utf8.odin" - -when ODIN_OS == "windows" { - import "core:atomics.odin" - import "core:opengl.odin" - import "core:thread.odin" - import win32 "core:sys/windows.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 - } -} - -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; - - switch 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; - - switch 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); - - switch 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); - - switch 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_Slot :: struct(Key, Value: type) { - occupied: bool, - hash: u32, - key: Key, - value: Value, - } - TABLE_SIZE_MIN :: 32; - Table :: struct(Key, Value: type) { - count: int, - allocator: Allocator, - slots: []Table_Slot(Key, Value), - } - - // 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(type_of(table.slots), max(capacity, TABLE_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), TABLE_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# 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/old_demos/demo008.odin b/examples/old_demos/demo008.odin deleted file mode 100644 index 7916cd5e9..000000000 --- a/examples/old_demos/demo008.odin +++ /dev/null @@ -1,778 +0,0 @@ -import "core:fmt.odin" -import "core:strconv.odin" -import "core:mem.odin" -import "core:bits.odin" -import "core:hash.odin" -import "core:math.odin" -import "core:math/rand.odin" -import "core:os.odin" -import "core:raw.odin" -import "core:sort.odin" -import "core:strings.odin" -import "core:types.odin" -import "core:utf16.odin" -import "core:utf8.odin" - -// File scope `when` statements -when ODIN_OS == "windows" { - import "core:atomics.odin" - import "core:thread.odin" - import win32 "core:sys/windows.odin" -} - -@(link_name="general_stuff") -general_stuff :: proc() { - fmt.println("# general_stuff"); - { // `do` for inline statements 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; - - _ = (^f32)(ptr); - // ^f32(ptr) == ^(f32(ptr)) - _ = cast(^f32)ptr; - - _ = (^f32)(ptr)^; - _ = (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 - } - - { // Multiple sized booleans - - x0: bool; // default - x1: b8 = true; - x2: b16 = false; - x3: b32 = true; - x4: b64 = false; - - fmt.printf("x1: %T = %v;\n", x1, x1); - fmt.printf("x2: %T = %v;\n", x2, x2); - fmt.printf("x3: %T = %v;\n", x3, x3); - fmt.printf("x4: %T = %v;\n", x4, x4); - - // Having specific sized booleans is very useful when dealing with foreign code - // and to enforce specific alignment for a boolean, especially within a struct - } - - { // `distinct` types - // Originally, all type declarations would create a distinct type unless #type_alias was present. - // Now the behaviour has been reversed. All type declarations create a type alias unless `distinct` is present. - // If the type expression is `struct`, `union`, `enum`, `proc`, or `bit_field`, the types will always been distinct. - - Int32 :: i32; - #assert(Int32 == i32); - - My_Int32 :: distinct i32; - #assert(My_Int32 != i32); - - My_Struct :: struct{x: int}; - #assert(My_Struct != struct{x: int}); - } -} - -default_struct_values :: proc() { - fmt.println("# default_struct_values"); - { - 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() { - fmt.println("\n# union_type"); - { - val: union{int, bool}; - val = 137; - if i, ok := val.(int); ok { - fmt.println(i); - } - val = true; - fmt.println(val); - - val = nil; - - switch 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; - - switch 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); - - switch 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); - - switch 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() { - fmt.println("# parametric_polymorphism"); - - 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 { - return mem.copy(&dst[0], &src[0], n*size_of(T)); - } - - 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_Slot :: struct(Key, Value: type) { - occupied: bool, - hash: u32, - key: Key, - value: Value, - } - TABLE_SIZE_MIN :: 32; - Table :: struct(Key, Value: type) { - count: int, - allocator: Allocator, - slots: []Table_Slot(Key, Value), - } - - // 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; - - context <- c { - table.slots = make_slice(type_of(table.slots), max(capacity, TABLE_SIZE_MIN)); - } - } - - expand :: proc(table: ^$T/Table) { - c := context; - if table.allocator.procedure != nil do c.allocator = table.allocator; - - context <- c { - old_slots := table.slots; - - cap := max(2*len(table.slots), TABLE_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(len(table.slots)) { - expand(table); - } - assert(table.count <= len(table.slots)); - - hash := get_hash(key); - index = int(hash % u32(len(table.slots))); - - for table.slots[index].occupied { - if index += 1; index >= len(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 len(table.slots) <= 0 do return -1; - - index := int(hash % u32(len(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 >= len(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" { - fmt.println("# threading_example"); - - unordered_remove :: proc(array: ^[dynamic]$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: ^[dynamic]$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([dynamic]^thread.Thread, 0, len(prefix_table)); - defer free(threads); - - for in 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; - } - } - } - } -} - -array_programming :: proc() { - fmt.println("# array_programming"); - { - a := [3]f32{1, 2, 3}; - b := [3]f32{5, 6, 7}; - c := a * b; - d := a + b; - e := 1 + (c - d) / 2; - fmt.printf("%.1f\n", e); // [0.5, 3.0, 6.5] - } - - { - a := [3]f32{1, 2, 3}; - b := swizzle(a, 2, 1, 0); - assert(b == [3]f32{3, 2, 1}); - - c := swizzle(a, 0, 0); - assert(c == [2]f32{1, 1}); - assert(c == 1); - } - - { - Vector3 :: distinct [3]f32; - a := Vector3{1, 2, 3}; - b := Vector3{5, 6, 7}; - c := (a * b)/2 + 1; - d := c.x + c.y + c.z; - fmt.printf("%.1f\n", d); // 22.0 - - cross :: proc(a, b: Vector3) -> Vector3 { - i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1); - j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0); - return i - j; - } - - blah :: proc(a: Vector3) -> f32 { - return a.x + a.y + a.z; - } - - x := cross(a, b); - fmt.println(x); - fmt.println(blah(x)); - } -} - - -using println in import "core:fmt.odin" - -using_in :: proc() { - fmt.println("# using in"); - using print in fmt; - - println("Hellope1"); - print("Hellope2\n"); - - Foo :: struct { - x, y: int, - b: bool, - } - f: Foo; - f.x, f.y = 123, 321; - println(f); - using x, y in f; - x, y = 456, 654; - println(f); -} - -named_proc_return_parameters :: proc() { - fmt.println("# named proc return parameters"); - - foo0 :: proc() -> int { - return 123; - } - foo1 :: proc() -> (a: int) { - a = 123; - return; - } - foo2 :: proc() -> (a, b: int) { - // Named return values act like variables within the scope - a = 321; - b = 567; - return b, a; - } - fmt.println("foo0 =", foo0()); // 123 - fmt.println("foo1 =", foo1()); // 123 - fmt.println("foo2 =", foo2()); // 567 321 -} - - -enum_export :: proc() { - fmt.println("# enum #export"); - - Foo :: enum #export {A, B, C}; - - f0 := A; - f1 := B; - f2 := C; - fmt.println(f0, f1, f2); -} - -explicit_procedure_overloading :: proc() { - fmt.println("# explicit procedure overloading"); - - add_ints :: proc(a, b: int) -> int { - x := a + b; - fmt.println("add_ints", x); - return x; - } - add_floats :: proc(a, b: f32) -> f32 { - x := a + b; - fmt.println("add_floats", x); - return x; - } - add_numbers :: proc(a: int, b: f32, c: u8) -> int { - x := int(a) + int(b) + int(c); - fmt.println("add_numbers", x); - return x; - } - - add :: proc[add_ints, add_floats, add_numbers]; - - add(int(1), int(2)); - add(f32(1), f32(2)); - add(int(1), f32(2), u8(3)); - - add(1, 2); // untyped ints coerce to int tighter than f32 - add(1.0, 2.0); // untyped floats coerce to f32 tighter than int - add(1, 2, 3); // three parameters - - // Ambiguous answers - // add(1.0, 2); - // add(1, 2.0); -} - -complete_switch :: proc() { - fmt.println("# complete_switch"); - { // enum - Foo :: enum #export { - A, - B, - C, - D, - } - - b := Foo.B; - f := Foo.A; - #complete switch f { - case A: fmt.println("A"); - case B: fmt.println("B"); - case C: fmt.println("C"); - case D: fmt.println("D"); - case: fmt.println("?"); - } - } - { // union - Foo :: union {int, bool}; - f: Foo = 123; - #complete switch in f { - case int: fmt.println("int"); - case bool: fmt.println("bool"); - case: - } - } -} - - -main :: proc() { - when true { - general_stuff(); - default_struct_values(); - union_type(); - parametric_polymorphism(); - threading_example(); - array_programming(); - using_in(); - named_proc_return_parameters(); - enum_export(); - explicit_procedure_overloading(); - complete_switch(); - } -} diff --git a/examples/old_demos/old_runtime.odin b/examples/old_demos/old_runtime.odin deleted file mode 100644 index e605e7820..000000000 --- a/examples/old_demos/old_runtime.odin +++ /dev/null @@ -1,412 +0,0 @@ -#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 - #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 deleted file mode 100644 index b8bbbb02d..000000000 --- a/examples/old_stuff/demo_backup.odin +++ /dev/null @@ -1,430 +0,0 @@ -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); - #assert(type_of(x) == int); - #assert(type_of(y) == f32); - #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/misc/old_demos/demo001.odin b/misc/old_demos/demo001.odin new file mode 100644 index 000000000..a3aea1cb7 --- /dev/null +++ b/misc/old_demos/demo001.odin @@ -0,0 +1,337 @@ +import "core:fmt.odin"; +import "core:os.odin"; +import "core: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_of(int); // by type + info = type_info_of(x); // by value + // See: runtime.odin + + match i in info.variant { + case Type_Info_Integer: + fmt.println("integer!"); + case Type_Info_Float: + fmt.println("float!"); + case: + 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(v1); + t2 := type_info_of(v2); + t3 := type_info_of(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_of(Fruit); + name := fruit_ti.variant.(Type_Info_Named).name; + info, _ := type_info_base(fruit_ti).variant.(Type_Info_Enum); + + fmt.printf("%s :: enum %T {\n", name, info.base); + for _, i in info.values { + 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) + // #assert(false) + // panic("Panic message goes here") +} + + + + diff --git a/misc/old_demos/demo002.odin b/misc/old_demos/demo002.odin new file mode 100644 index 000000000..a790aadf3 --- /dev/null +++ b/misc/old_demos/demo002.odin @@ -0,0 +1,879 @@ +// Demo 002 +export "core:fmt.odin"; +export "core:math.odin"; +export "core:mem.odin"; +// export "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 do 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 do 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? + + push_allocator default_allocator() { + a := new(int); + defer free(a); + + // Do whatever + + } + } + + { + a: int = 123; + b: type_of(a) = 321; + + // NOTE(bill): This matches the current naming scheme + // size_of + // align_of + // offset_of + // + // size_of_val + // align_of_val + // offset_of_val + // type_of_val + } + + { + // Compile time assert + COND :: true; + #assert(COND); + // #assert(!COND) + + // Runtime assert + x := true; + assert(x); + // assert(!x); + } + + { + x: ^u32 = 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 + + case: + print("default!\n"); + } + + + + match x := 1.5; x { + case 1.5: + print("1.5!\n"); + // break by default + case TAU: + print("τ!\n"); + case: + print("default!\n"); + } + + + + match x := "Hello"; x { + case "Hello": + print("greeting\n"); + // break by default + case "Goodbye": + print("farewell\n"); + case: + 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"); + case: + 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 do 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^ += 1; + 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() { + { + Entity_Kind :: enum { + INVALID, + FROG, + GIRAFFE, + HELICOPTER, + } + + Entity :: struct { + kind: Entity_Kind + using data: struct #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 = Entity_Kind.FROG; + e.frog.jump_height = 12; + + f: type_of(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 + } + + { + 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, + } + Entity :: union {Frog, Giraffe, Helicopter}; + + 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"); + case: + 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 {}; + + Entity_Kind :: enum { + Invalid, + Constant, + Variable, + Using_Variable, + TypeName, + Procedure, + Builtin, + Count, + } + + Guid :: i64; + Entity :: struct { + + kind: Entity_Kind, + guid: Guid, + + scope: ^Scope, + token: Token, + type_: ^Type, + + using data: struct #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 + }, + Using_Variable: 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 { + } + + + 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 + } + Using_Variable :: struct { + } + TypeName :: struct { + } + Procedure :: struct { + used: bool, + } + Builtin :: struct { + id: int, + } + + Entity :: struct { + guid: Guid, + + scope: ^Scope, + token: Token, + type_: ^Type, + + variant: union {Constant, Variable, Using_Variable, TypeName, Procedure, Builtin}, + } + + e := Entity{ + variant = 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 :: struct #raw_union { + using xy_: struct { x, y: f32 }, + e: [2]f32, + v: [vector 2]f32, + } + + Vector3 :: struct #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/misc/old_demos/demo004.odin b/misc/old_demos/demo004.odin new file mode 100644 index 000000000..c9acc9a15 --- /dev/null +++ b/misc/old_demos/demo004.odin @@ -0,0 +1,66 @@ +import "core:fmt.odin"; +import "core:utf8.odin"; +import "core:hash.odin"; +import "core:mem.odin"; + +main :: proc() { + { // New Standard Library stuff + s := "Hello"; + fmt.println(s, + utf8.valid_string(s), + hash.murmur64(cast([]u8)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.destroy_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/misc/old_demos/demo005.odin b/misc/old_demos/demo005.odin new file mode 100644 index 000000000..c8273b03b --- /dev/null +++ b/misc/old_demos/demo005.odin @@ -0,0 +1,283 @@ +import "core:fmt.odin"; +import "core:utf8.odin"; +// import "core:atomic.odin"; +// import "core:hash.odin"; +// import "core:math.odin"; +// import "core:mem.odin"; +// import "core:opengl.odin"; +// import "core:os.odin"; +// import "core:sync.odin"; +// import win32 "core: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; #assert(size_of(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; + var.x = 123; + x = 123; + + + foo :: proc(using pt: Prefix_Type) { + } + + + + // 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(); +} + +when ODIN_OS == "windows" { + foreign_system_library win32_user "user32.lib"; +} +// 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() { + foreign win32_user { + ShowWindow :: proc(hwnd: rawptr, cmd_show: i32) -> i32 ---; + show_window :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #link_name "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 { + #assert(type_of(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/misc/old_demos/demo006.odin b/misc/old_demos/demo006.odin new file mode 100644 index 000000000..c2f64151b --- /dev/null +++ b/misc/old_demos/demo006.odin @@ -0,0 +1,310 @@ +// import "core:atomic.odin"; +import "core:hash.odin"; +import "core:mem.odin"; +import "core:opengl.odin"; +import "core:strconv.odin"; +import "core:sync.odin"; +import win32 "core:sys/windows.odin"; + +import "core:fmt.odin"; +import "core:os.odin"; +import "core: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: + * `#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 += 1 { + } + } + + { + 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_of(int); + match i in t.variant { + case Type_Info_Integer, Type_Info_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]u8; + s: []u8; + s = buf[..0]; // == buf[0..0]; + fmt.println("count =", len(s)); + fmt.println("capacity =", cap(s)); + append(&s, 1, 2, 3); + fmt.println(s); + + s = buf[1..2..3]; + fmt.println("count =", len(s)); + fmt.println("capacity =", cap(s)); + fmt.println(s); + + clear(&s); // Sets count to zero + } + + { + Foo :: struct { + x, y, z: f32, + ok: bool, + flags: u32, + } + foo_array: [256]Foo; + foo_as_bytes: []u8 = mem.slice_to_bytes(foo_array[..]); + // Useful for things like + // os.write(handle, foo_as_bytes); + + foo_slice := mem.slice_ptr(cast(^Foo)&foo_as_bytes[0], len(foo_as_bytes)/size_of(Foo), cap(foo_as_bytes)/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); + } + + #assert(size_of([vector 7]bool) >= size_of([7]bool)); + #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 `string` + + data: [256]u8; + str := fmt.bprintf(data[..], "Hellope %d %s %c", 123, "others", '!'); + fmt.println(str); + } + + { + 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}; + + // Variants + Frog :: struct { + ribbit_volume: f32, + jump_height: f32, + } + Door :: struct { + openness: f32, + } + Map :: struct { + width, height: f32, + place_positions: []Vector3, + place_names: []string, + } + + Entity :: struct { + // Common Fields + id: u64, + name: string, + using position: Vector3, + orientation: Quaternion, + flags: u32, + + variant: union { Frog, Door, Map }, + } + + entity: Entity; + entity.id = 1337; + // implicit conversion from variant to base type + entity.variant = Frog{ + ribbit_volume = 0.5, + jump_height = 2.1, + /*other data */ + }; + + entity.name = "Frank"; + entity.position = Vector3{1, 4, 9}; + + match e in entity.variant { + case Frog: + fmt.println("Ribbit"); + case Door: + fmt.println("Creak"); + case Map: + fmt.println("Rustle"); + case: + fmt.println("Just a normal entity"); + } + + if frog, ok := entity.variant.(Frog); ok { + fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, entity.position); + } + + // Panics if not the correct type + frog: Frog; + frog = entity.variant.(Frog); + frog, _ = entity.variant.(Frog); // ignore error and force cast + } +} +} + diff --git a/misc/old_demos/demo007.odin b/misc/old_demos/demo007.odin new file mode 100644 index 000000000..d19446ecb --- /dev/null +++ b/misc/old_demos/demo007.odin @@ -0,0 +1,570 @@ +import "core:fmt.odin" +import "core:strconv.odin" +import "core:mem.odin" +import "core:bits.odin" +import "core:hash.odin" +import "core:math.odin" +import "core:os.odin" +import "core:raw.odin" +import "core:sort.odin" +import "core:strings.odin" +import "core:types.odin" +import "core:utf16.odin" +import "core:utf8.odin" + +when ODIN_OS == "windows" { + import "core:atomics.odin" + import "core:opengl.odin" + import "core:thread.odin" + import win32 "core:sys/windows.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 + } +} + +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; + + switch 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; + + switch 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); + + switch 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); + + switch 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_Slot :: struct(Key, Value: type) { + occupied: bool, + hash: u32, + key: Key, + value: Value, + } + TABLE_SIZE_MIN :: 32; + Table :: struct(Key, Value: type) { + count: int, + allocator: Allocator, + slots: []Table_Slot(Key, Value), + } + + // 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(type_of(table.slots), max(capacity, TABLE_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), TABLE_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# 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/misc/old_demos/demo008.odin b/misc/old_demos/demo008.odin new file mode 100644 index 000000000..7916cd5e9 --- /dev/null +++ b/misc/old_demos/demo008.odin @@ -0,0 +1,778 @@ +import "core:fmt.odin" +import "core:strconv.odin" +import "core:mem.odin" +import "core:bits.odin" +import "core:hash.odin" +import "core:math.odin" +import "core:math/rand.odin" +import "core:os.odin" +import "core:raw.odin" +import "core:sort.odin" +import "core:strings.odin" +import "core:types.odin" +import "core:utf16.odin" +import "core:utf8.odin" + +// File scope `when` statements +when ODIN_OS == "windows" { + import "core:atomics.odin" + import "core:thread.odin" + import win32 "core:sys/windows.odin" +} + +@(link_name="general_stuff") +general_stuff :: proc() { + fmt.println("# general_stuff"); + { // `do` for inline statements 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; + + _ = (^f32)(ptr); + // ^f32(ptr) == ^(f32(ptr)) + _ = cast(^f32)ptr; + + _ = (^f32)(ptr)^; + _ = (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 + } + + { // Multiple sized booleans + + x0: bool; // default + x1: b8 = true; + x2: b16 = false; + x3: b32 = true; + x4: b64 = false; + + fmt.printf("x1: %T = %v;\n", x1, x1); + fmt.printf("x2: %T = %v;\n", x2, x2); + fmt.printf("x3: %T = %v;\n", x3, x3); + fmt.printf("x4: %T = %v;\n", x4, x4); + + // Having specific sized booleans is very useful when dealing with foreign code + // and to enforce specific alignment for a boolean, especially within a struct + } + + { // `distinct` types + // Originally, all type declarations would create a distinct type unless #type_alias was present. + // Now the behaviour has been reversed. All type declarations create a type alias unless `distinct` is present. + // If the type expression is `struct`, `union`, `enum`, `proc`, or `bit_field`, the types will always been distinct. + + Int32 :: i32; + #assert(Int32 == i32); + + My_Int32 :: distinct i32; + #assert(My_Int32 != i32); + + My_Struct :: struct{x: int}; + #assert(My_Struct != struct{x: int}); + } +} + +default_struct_values :: proc() { + fmt.println("# default_struct_values"); + { + 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() { + fmt.println("\n# union_type"); + { + val: union{int, bool}; + val = 137; + if i, ok := val.(int); ok { + fmt.println(i); + } + val = true; + fmt.println(val); + + val = nil; + + switch 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; + + switch 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); + + switch 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); + + switch 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() { + fmt.println("# parametric_polymorphism"); + + 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 { + return mem.copy(&dst[0], &src[0], n*size_of(T)); + } + + 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_Slot :: struct(Key, Value: type) { + occupied: bool, + hash: u32, + key: Key, + value: Value, + } + TABLE_SIZE_MIN :: 32; + Table :: struct(Key, Value: type) { + count: int, + allocator: Allocator, + slots: []Table_Slot(Key, Value), + } + + // 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; + + context <- c { + table.slots = make_slice(type_of(table.slots), max(capacity, TABLE_SIZE_MIN)); + } + } + + expand :: proc(table: ^$T/Table) { + c := context; + if table.allocator.procedure != nil do c.allocator = table.allocator; + + context <- c { + old_slots := table.slots; + + cap := max(2*len(table.slots), TABLE_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(len(table.slots)) { + expand(table); + } + assert(table.count <= len(table.slots)); + + hash := get_hash(key); + index = int(hash % u32(len(table.slots))); + + for table.slots[index].occupied { + if index += 1; index >= len(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 len(table.slots) <= 0 do return -1; + + index := int(hash % u32(len(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 >= len(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" { + fmt.println("# threading_example"); + + unordered_remove :: proc(array: ^[dynamic]$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: ^[dynamic]$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([dynamic]^thread.Thread, 0, len(prefix_table)); + defer free(threads); + + for in 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; + } + } + } + } +} + +array_programming :: proc() { + fmt.println("# array_programming"); + { + a := [3]f32{1, 2, 3}; + b := [3]f32{5, 6, 7}; + c := a * b; + d := a + b; + e := 1 + (c - d) / 2; + fmt.printf("%.1f\n", e); // [0.5, 3.0, 6.5] + } + + { + a := [3]f32{1, 2, 3}; + b := swizzle(a, 2, 1, 0); + assert(b == [3]f32{3, 2, 1}); + + c := swizzle(a, 0, 0); + assert(c == [2]f32{1, 1}); + assert(c == 1); + } + + { + Vector3 :: distinct [3]f32; + a := Vector3{1, 2, 3}; + b := Vector3{5, 6, 7}; + c := (a * b)/2 + 1; + d := c.x + c.y + c.z; + fmt.printf("%.1f\n", d); // 22.0 + + cross :: proc(a, b: Vector3) -> Vector3 { + i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1); + j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0); + return i - j; + } + + blah :: proc(a: Vector3) -> f32 { + return a.x + a.y + a.z; + } + + x := cross(a, b); + fmt.println(x); + fmt.println(blah(x)); + } +} + + +using println in import "core:fmt.odin" + +using_in :: proc() { + fmt.println("# using in"); + using print in fmt; + + println("Hellope1"); + print("Hellope2\n"); + + Foo :: struct { + x, y: int, + b: bool, + } + f: Foo; + f.x, f.y = 123, 321; + println(f); + using x, y in f; + x, y = 456, 654; + println(f); +} + +named_proc_return_parameters :: proc() { + fmt.println("# named proc return parameters"); + + foo0 :: proc() -> int { + return 123; + } + foo1 :: proc() -> (a: int) { + a = 123; + return; + } + foo2 :: proc() -> (a, b: int) { + // Named return values act like variables within the scope + a = 321; + b = 567; + return b, a; + } + fmt.println("foo0 =", foo0()); // 123 + fmt.println("foo1 =", foo1()); // 123 + fmt.println("foo2 =", foo2()); // 567 321 +} + + +enum_export :: proc() { + fmt.println("# enum #export"); + + Foo :: enum #export {A, B, C}; + + f0 := A; + f1 := B; + f2 := C; + fmt.println(f0, f1, f2); +} + +explicit_procedure_overloading :: proc() { + fmt.println("# explicit procedure overloading"); + + add_ints :: proc(a, b: int) -> int { + x := a + b; + fmt.println("add_ints", x); + return x; + } + add_floats :: proc(a, b: f32) -> f32 { + x := a + b; + fmt.println("add_floats", x); + return x; + } + add_numbers :: proc(a: int, b: f32, c: u8) -> int { + x := int(a) + int(b) + int(c); + fmt.println("add_numbers", x); + return x; + } + + add :: proc[add_ints, add_floats, add_numbers]; + + add(int(1), int(2)); + add(f32(1), f32(2)); + add(int(1), f32(2), u8(3)); + + add(1, 2); // untyped ints coerce to int tighter than f32 + add(1.0, 2.0); // untyped floats coerce to f32 tighter than int + add(1, 2, 3); // three parameters + + // Ambiguous answers + // add(1.0, 2); + // add(1, 2.0); +} + +complete_switch :: proc() { + fmt.println("# complete_switch"); + { // enum + Foo :: enum #export { + A, + B, + C, + D, + } + + b := Foo.B; + f := Foo.A; + #complete switch f { + case A: fmt.println("A"); + case B: fmt.println("B"); + case C: fmt.println("C"); + case D: fmt.println("D"); + case: fmt.println("?"); + } + } + { // union + Foo :: union {int, bool}; + f: Foo = 123; + #complete switch in f { + case int: fmt.println("int"); + case bool: fmt.println("bool"); + case: + } + } +} + + +main :: proc() { + when true { + general_stuff(); + default_struct_values(); + union_type(); + parametric_polymorphism(); + threading_example(); + array_programming(); + using_in(); + named_proc_return_parameters(); + enum_export(); + explicit_procedure_overloading(); + complete_switch(); + } +} diff --git a/misc/old_demos/old_runtime.odin b/misc/old_demos/old_runtime.odin new file mode 100644 index 000000000..e605e7820 --- /dev/null +++ b/misc/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 + #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/misc/old_stuff/demo_backup.odin b/misc/old_stuff/demo_backup.odin new file mode 100644 index 000000000..b8bbbb02d --- /dev/null +++ b/misc/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); + #assert(type_of(x) == int); + #assert(type_of(y) == f32); + #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/misc/shell.bat b/misc/shell.bat index 36db84570..85a7949c6 100644 --- a/misc/shell.bat +++ b/misc/shell.bat @@ -1,8 +1,8 @@ @echo off rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x86 1> NUL -call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL -rem call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL +rem call "C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\vcvarsall.bat" x64 1> NUL +call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 1> NUL rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x86 1> NUL rem call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 1> NUL set _NO_DEBUG_HEAP=1 -- cgit v1.2.3