diff options
| author | gingerBill <bill@gingerbill.org> | 2019-10-26 10:40:17 +0100 |
|---|---|---|
| committer | gingerBill <bill@gingerbill.org> | 2019-10-26 10:40:17 +0100 |
| commit | c60fb10a6a0b668e9dab7e4cb899664f5e32419e (patch) | |
| tree | 35f59e5242c9a21c20b00c30402239460fb2c4ae /misc | |
| parent | 7140f4291546748adf2ddb977f3605f7aab31856 (diff) | |
Move old demos and old stuff to /misc
Diffstat (limited to 'misc')
| -rw-r--r-- | misc/old_demos/demo001.odin | 337 | ||||
| -rw-r--r-- | misc/old_demos/demo002.odin | 879 | ||||
| -rw-r--r-- | misc/old_demos/demo004.odin | 66 | ||||
| -rw-r--r-- | misc/old_demos/demo005.odin | 283 | ||||
| -rw-r--r-- | misc/old_demos/demo006.odin | 310 | ||||
| -rw-r--r-- | misc/old_demos/demo007.odin | 570 | ||||
| -rw-r--r-- | misc/old_demos/demo008.odin | 778 | ||||
| -rw-r--r-- | misc/old_demos/old_runtime.odin | 412 | ||||
| -rw-r--r-- | misc/old_stuff/demo_backup.odin | 430 | ||||
| -rw-r--r-- | misc/shell.bat | 4 |
10 files changed, 4067 insertions, 2 deletions
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 |