diff --git a/examples/demo.odin b/examples/demo.odin new file mode 100644 index 000000000..de0dce69c --- /dev/null +++ b/examples/demo.odin @@ -0,0 +1,594 @@ +import "fmt.odin"; +import "strconv.odin"; +import "mem.odin"; +import "thread.odin" when ODIN_OS == "windows"; +import win32 "sys/windows.odin" when ODIN_OS == "windows"; +import "atomics.odin"; +import "bits.odin"; +import "hash.odin"; +import "math.odin"; +import "opengl.odin"; +import "os.odin"; +import "raw.odin"; +import "sort.odin"; +import "strings.odin"; +import "sync.odin"; +import "types.odin"; +import "utf8.odin"; +import "utf16.odin"; + +general_stuff :: proc() { + { // `do` for inline statmes rather than block + foo :: proc() do fmt.println("Foo!"); + if false do foo(); + for false do foo(); + when false do foo(); + + if false do foo(); + else do foo(); + } + + { // Removal of `++` and `--` (again) + x: int; + x += 1; + x -= 1; + } + { // Casting syntaxes + i := i32(137); + ptr := &i; + + fp1 := (^f32)(ptr); + // ^f32(ptr) == ^(f32(ptr)) + fp2 := cast(^f32)ptr; + + f1 := (^f32)(ptr)^; + f2 := (cast(^f32)ptr)^; + + // Questions: Should there be two ways to do it? + } + + /* + * Remove *_val_of built-in procedures + * size_of, align_of, offset_of + * type_of, type_info_of + */ + + { // `expand_to_tuple` built-in procedure + Foo :: struct { + x: int; + b: bool; + } + f := Foo{137, true}; + x, b := expand_to_tuple(f); + fmt.println(f); + fmt.println(x, b); + fmt.println(expand_to_tuple(f)); + } + + { + // .. half-closed range + // ... open range + + for in 0..2 {} // 0, 1 + for in 0...2 {} // 0, 1, 2 + } +} + +nested_struct_declarations :: proc() { + { + FooInteger :: int; + Foo :: struct { + i: FooInteger; + }; + f := Foo{FooInteger(137)}; + } + { + Foo :: struct { + Integer :: int; + + i: Integer; + } + f := Foo{Foo.Integer(137)}; + + } +} + +default_struct_values :: proc() { + { + Vector3 :: struct { + x: f32; + y: f32; + z: f32; + } + v: Vector3; + fmt.println(v); + } + { + // Default values must be constants + Vector3 :: struct { + x: f32 = 1; + y: f32 = 4; + z: f32 = 9; + } + v: Vector3; + fmt.println(v); + + v = Vector3{}; + fmt.println(v); + + // Uses the same semantics as a default values in a procedure + v = Vector3{137}; + fmt.println(v); + + v = Vector3{z = 137}; + fmt.println(v); + } + + { + Vector3 :: struct { + x := 1.0; + y := 4.0; + z := 9.0; + } + stack_default: Vector3; + stack_literal := Vector3{}; + heap_one := new(Vector3); defer free(heap_one); + heap_two := new_clone(Vector3{}); defer free(heap_two); + + fmt.println("stack_default - ", stack_default); + fmt.println("stack_literal - ", stack_literal); + fmt.println("heap_one - ", heap_one^); + fmt.println("heap_two - ", heap_two^); + + + N :: 4; + stack_array: [N]Vector3; + heap_array := new([N]Vector3); defer free(heap_array); + heap_slice := make([]Vector3, N); defer free(heap_slice); + fmt.println("stack_array[1] - ", stack_array[1]); + fmt.println("heap_array[1] - ", heap_array[1]); + fmt.println("heap_slice[1] - ", heap_slice[1]); + } +} + + + + +union_type :: proc() { + { + val: union{int, bool}; + val = 137; + if i, ok := val.(int); ok { + fmt.println(i); + } + val = true; + fmt.println(val); + + val = nil; + + match v in val { + case int: fmt.println("int", v); + case bool: fmt.println("bool", v); + case: fmt.println("nil"); + } + } + { + // There is a duality between `any` and `union` + // An `any` has a pointer to the data and allows for any type (open) + // A `union` has as binary blob to store the data and allows only certain types (closed) + // The following code is with `any` but has the same syntax + val: any; + val = 137; + if i, ok := val.(int); ok { + fmt.println(i); + } + val = true; + fmt.println(val); + + val = nil; + + match v in val { + case int: fmt.println("int", v); + case bool: fmt.println("bool", v); + case: fmt.println("nil"); + } + } + + Vector3 :: struct { + x, y, z: f32; + }; + Quaternion :: struct { + x, y, z: f32; + w: f32 = 1; + }; + + // More realistic examples + { + // NOTE(bill): For the above basic examples, you may not have any + // particular use for it. However, my main use for them is not for these + // simple cases. My main use is for hierarchical types. Many prefer + // subtyping, embedding the base data into the derived types. Below is + // an example of this for a basic game Entity. + + Entity :: struct { + id: u64; + name: string; + position: Vector3; + orientation: Quaternion; + + derived: any; + } + + Frog :: struct { + using entity: Entity; + jump_height: f32; + } + + Monster :: struct { + using entity: Entity; + is_robot: bool; + is_zombie: bool; + } + + // See `parametric_polymorphism` procedure for details + new_entity :: proc(T: type) -> ^Entity { + t := new(T); + t.derived = t^; + return t; + } + + entity := new_entity(Monster); + + match e in entity.derived { + case Frog: + fmt.println("Ribbit"); + case Monster: + if e.is_robot do fmt.println("Robotic"); + if e.is_zombie do fmt.println("Grrrr!"); + } + } + + { + // NOTE(bill): A union can be used to achieve something similar. Instead + // of embedding the base data into the derived types, the derived data + // in embedded into the base type. Below is the same example of the + // basic game Entity but using an union. + + Entity :: struct { + id: u64; + name: string; + position: Vector3; + orientation: Quaternion; + + derived: union {Frog, Monster}; + } + + Frog :: struct { + using entity: ^Entity; + jump_height: f32; + } + + Monster :: struct { + using entity: ^Entity; + is_robot: bool; + is_zombie: bool; + } + + // See `parametric_polymorphism` procedure for details + new_entity :: proc(T: type) -> ^Entity { + t := new(Entity); + t.derived = T{entity = t}; + return t; + } + + entity := new_entity(Monster); + + match e in entity.derived { + case Frog: + fmt.println("Ribbit"); + case Monster: + if e.is_robot do fmt.println("Robotic"); + if e.is_zombie do fmt.println("Grrrr!"); + } + + // NOTE(bill): As you can see, the usage code has not changed, only its + // memory layout. Both approaches have their own advantages but they can + // be used together to achieve different results. The subtyping approach + // can allow for a greater control of the memory layout and memory + // allocation, e.g. storing the derivatives together. However, this is + // also its disadvantage. You must either preallocate arrays for each + // derivative separation (which can be easily missed) or preallocate a + // bunch of "raw" memory; determining the maximum size of the derived + // types would require the aid of metaprogramming. Unions solve this + // particular problem as the data is stored with the base data. + // Therefore, it is possible to preallocate, e.g. [100]Entity. + + // It should be noted that the union approach can have the same memory + // layout as the any and with the same type restrictions by using a + // pointer type for the derivatives. + + /* + Entity :: struct { + ... + derived: union{^Frog, ^Monster}; + } + + Frog :: struct { + using entity: Entity; + ... + } + Monster :: struct { + using entity: Entity; + ... + + } + new_entity :: proc(T: type) -> ^Entity { + t := new(T); + t.derived = t; + return t; + } + */ + } +} + +parametric_polymorphism :: proc() { + print_value :: proc(value: $T) { + fmt.printf("print_value: %T %v\n", value, value); + } + + v1: int = 1; + v2: f32 = 2.1; + v3: f64 = 3.14; + v4: string = "message"; + + print_value(v1); + print_value(v2); + print_value(v3); + print_value(v4); + + fmt.println(); + + add :: proc(p, q: $T) -> T { + x: T = p + q; + return x; + } + + a := add(3, 4); + fmt.printf("a: %T = %v\n", a, a); + + b := add(3.2, 4.3); + fmt.printf("b: %T = %v\n", b, b); + + // This is how `new` is implemented + alloc_type :: proc(T: type) -> ^T { + t := cast(^T)alloc(size_of(T), align_of(T)); + t^ = T{}; // Use default initialization value + return t; + } + + copy_slice :: proc(dst, src: []$T) -> int { + n := min(len(dst), len(src)); + if n > 0 { + mem.copy(&dst[0], &src[0], n*size_of(T)); + } + return n; + } + + double_params :: proc(a: $A, b: $B) -> A { + return a + A(b); + } + + fmt.println(double_params(12, 1.345)); + + + + { // Polymorphic Types and Type Specialization + Table :: struct(Key, Value: type) { + Slot :: struct { + occupied: bool; + hash: u32; + key: Key; + value: Value; + } + SIZE_MIN :: 32; + + count: int; + allocator: Allocator; + slots: []Slot; + } + + // Only allow types that are specializations of a (polymorphic) slice + make_slice :: proc(T: type/[]$E, len: int) -> T { + return make(T, len); + } + + + // Only allow types that are specializations of `Table` + allocate :: proc(table: ^$T/Table, capacity: int) { + c := context; + if table.allocator.procedure != nil do c.allocator = table.allocator; + + push_context c { + table.slots = make_slice([]T.Slot, max(capacity, T.SIZE_MIN)); + } + } + + expand :: proc(table: ^$T/Table) { + c := context; + if table.allocator.procedure != nil do c.allocator = table.allocator; + + push_context c { + old_slots := table.slots; + + cap := max(2*cap(table.slots), T.SIZE_MIN); + allocate(table, cap); + + for s in old_slots do if s.occupied { + put(table, s.key, s.value); + } + + free(old_slots); + } + } + + // Polymorphic determination of a polymorphic struct + // put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) { + put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) { + hash := get_hash(key); // Ad-hoc method which would fail in a different scope + index := find_index(table, key, hash); + if index < 0 { + if f64(table.count) >= 0.75*f64(cap(table.slots)) { + expand(table); + } + assert(table.count <= cap(table.slots)); + + hash := get_hash(key); + index = int(hash % u32(cap(table.slots))); + + for table.slots[index].occupied { + if index += 1; index >= cap(table.slots) { + index = 0; + } + } + + table.count += 1; + } + + slot := &table.slots[index]; + slot.occupied = true; + slot.hash = hash; + slot.key = key; + slot.value = value; + } + + + // find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) { + find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) { + hash := get_hash(key); + index := find_index(table, key, hash); + if index < 0 { + return Value{}, false; + } + return table.slots[index].value, true; + } + + find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int { + if cap(table.slots) <= 0 do return -1; + + index := int(hash % u32(cap(table.slots))); + for table.slots[index].occupied { + if table.slots[index].hash == hash { + if table.slots[index].key == key { + return index; + } + } + + if index += 1; index >= cap(table.slots) { + index = 0; + } + } + + return -1; + } + + get_hash :: proc(s: string) -> u32 { // fnv32a + h: u32 = 0x811c9dc5; + for i in 0..len(s) { + h = (h ~ u32(s[i])) * 0x01000193; + } + return h; + } + + + table: Table(string, int); + + for i in 0..36 do put(&table, "Hellope", i); + for i in 0..42 do put(&table, "World!", i); + + found, _ := find(&table, "Hellope"); + fmt.printf("`found` is %v\n", found); + + found, _ = find(&table, "World!"); + fmt.printf("`found` is %v\n", found); + + // I would not personally design a hash table like this in production + // but this is a nice basic example + // A better approach would either use a `u64` or equivalent for the key + // and let the user specify the hashing function or make the user store + // the hashing procedure with the table + } +} + + + + +prefix_table := [...]string{ + "White", + "Red", + "Green", + "Blue", + "Octarine", + "Black", +}; + +threading_example :: proc() { + when ODIN_OS == "windows" { + unordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) { + __bounds_check_error_loc(loc, index, len(array)); + array[index] = array[len(array)-1]; + pop(array); + } + ordered_remove :: proc(array: ^[]$T, index: int, loc := #caller_location) { + __bounds_check_error_loc(loc, index, len(array)); + copy(array[index..], array[index+1..]); + pop(array); + } + + worker_proc :: proc(t: ^thread.Thread) -> int { + for iteration in 1...5 { + fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration); + fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration); + // win32.sleep(1); + } + return 0; + } + + threads := make([]^thread.Thread, 0, len(prefix_table)); + defer free(threads); + + for i in 0..len(prefix_table) { + if t := thread.create(worker_proc); t != nil { + t.init_context = context; + t.use_init_context = true; + t.user_index = len(threads); + append(&threads, t); + thread.start(t); + } + } + + for len(threads) > 0 { + for i := 0; i < len(threads); /**/ { + if t := threads[i]; thread.is_done(t) { + fmt.printf("Thread %d is done\n", t.user_index); + thread.destroy(t); + + ordered_remove(&threads, i); + } else { + i += 1; + } + } + } + } +} + +main :: proc() { + when false { + fmt.println("\n# general_stuff"); general_stuff(); + fmt.println("\n# nested_struct_declarations"); nested_struct_declarations(); + fmt.println("\n# default_struct_values"); default_struct_values(); + fmt.println("\n# union_type"); union_type(); + fmt.println("\n# parametric_polymorphism"); parametric_polymorphism(); + fmt.println("\n# threading_example"); threading_example(); + } +} + diff --git a/examples/factorial.odin b/examples/factorial.odin new file mode 100644 index 000000000..af39bbac1 --- /dev/null +++ b/examples/factorial.odin @@ -0,0 +1,20 @@ +import "fmt.odin"; + +main :: proc() { + recursive_factorial :: proc(i: u64) -> u64 { + if i < 2 do return 1; + return i * recursive_factorial(i-1); + } + + loop_factorial :: proc(i: u64) -> u64 { + result: u64 = 1; + for n in 2..i { + result *= n; + } + return result; + } + + + fmt.println(recursive_factorial(12)); + fmt.println(loop_factorial(12)); +} diff --git a/examples/game.odin b/examples/game.odin new file mode 100644 index 000000000..1f09e9209 --- /dev/null +++ b/examples/game.odin @@ -0,0 +1,221 @@ +import win32 "sys/windows.odin" when ODIN_OS == "windows"; +import wgl "sys/wgl.odin" when ODIN_OS == "windows"; +import "fmt.odin"; +import "math.odin"; +import "os.odin"; +import gl "opengl.odin"; + +TWO_HEARTS :: '💕'; + +win32_perf_count_freq := win32.get_query_performance_frequency(); +time_now :: proc() -> f64 { + assert(win32_perf_count_freq != 0); + + counter: i64; + win32.query_performance_counter(&counter); + return f64(counter) / f64(win32_perf_count_freq); +} +win32_print_last_error :: proc() { + err_code := win32.get_last_error(); + if err_code != 0 { + fmt.println("get_last_error: ", err_code); + } +} + +// Yuk! +to_c_string :: proc(s: string) -> []u8 { + c_str := make([]u8, len(s)+1); + copy(c_str, cast([]u8)s); + c_str[len(s)] = 0; + return c_str; +} + + +Window :: struct { + width, height: int; + wc: win32.Wnd_Class_Ex_A; + dc: win32.Hdc; + hwnd: win32.Hwnd; + opengl_context, rc: wgl.Hglrc; + c_title: []u8; +} + +make_window :: proc(title: string, msg, height: int, window_proc: win32.Wnd_Proc) -> (Window, bool) { + using win32; + + w: Window; + w.width, w.height = msg, height; + + class_name := "Win32-Odin-Window\x00"; + c_class_name := &class_name[0]; + if title[len(title)-1] != 0 { + w.c_title = to_c_string(title); + } else { + w.c_title = cast([]u8)title; + } + + instance := get_module_handle_a(nil); + + w.wc = Wnd_Class_Ex_A{ + size = size_of(Wnd_Class_Ex_A), + style = CS_VREDRAW | CS_HREDRAW, + instance = Hinstance(instance), + class_name = c_class_name, + wnd_proc = window_proc, + }; + + if register_class_ex_a(&w.wc) == 0 { + win32_print_last_error(); + return w, false; + } + + w.hwnd = create_window_ex_a(0, + c_class_name, &w.c_title[0], + WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, + CW_USEDEFAULT, CW_USEDEFAULT, + i32(w.width), i32(w.height), + nil, nil, instance, nil); + + if w.hwnd == nil { + win32_print_last_error(); + return w, false; + } + + w.dc = get_dc(w.hwnd); + + { + pfd := Pixel_Format_Descriptor{ + size = size_of(Pixel_Format_Descriptor), + version = 1, + flags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + pixel_type = PFD_TYPE_RGBA, + color_bits = 32, + alpha_bits = 8, + depth_bits = 24, + stencil_bits = 8, + layer_type = PFD_MAIN_PLANE, + }; + + set_pixel_format(w.dc, choose_pixel_format(w.dc, &pfd), nil); + w.opengl_context = wgl.create_context(w.dc); + wgl.make_current(w.dc, w.opengl_context); + + attribs := [8]i32{ + wgl.CONTEXT_MAJOR_VERSION_ARB, 2, + wgl.CONTEXT_MINOR_VERSION_ARB, 1, + wgl.CONTEXT_PROFILE_MASK_ARB, wgl.CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, + 0, // NOTE(bill): tells the that :: proc this is the end of attribs + }; + + wgl_str := "wglCreateContextAttribsARB\x00"; + wglCreateContextAttribsARB := cast(wgl.Create_Context_Attribs_ARB_Type)wgl.get_proc_address(&wgl_str[0]); + w.rc = wglCreateContextAttribsARB(w.dc, nil, &attribs[0]); + wgl.make_current(w.dc, w.rc); + swap_buffers(w.dc); + } + + return w, true; +} + +destroy_window :: proc(w: ^Window) { + free(w.c_title); +} + +display_window :: proc(w: ^Window) { + win32.swap_buffers(w.dc); +} + + +run :: proc() { + using math; + + win32_proc :: proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline { + using win32; + if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT { + os.exit(0); + return 0; + } + return def_window_proc_a(hwnd, msg, wparam, lparam); + } + + window, window_success := make_window("Odin Language Demo", 854, 480, cast(win32.Wnd_Proc)win32_proc); + if !window_success { + return; + } + defer destroy_window(&window); + + gl.init(); + + using win32; + + prev_time := time_now(); + running := true; + + pos := Vec2{100, 100}; + + for running { + curr_time := time_now(); + dt := f32(curr_time - prev_time); + prev_time = curr_time; + + msg: Msg; + for peek_message_a(&msg, nil, 0, 0, PM_REMOVE) > 0 { + if msg.message == WM_QUIT { + running = false; + } + translate_message(&msg); + dispatch_message_a(&msg); + } + + if is_key_down(Key_Code.Escape) { + running = false; + } + + { + SPEED :: 500; + v: Vec2; + + if is_key_down(Key_Code.Right) do v[0] += 1; + if is_key_down(Key_Code.Left) do v[0] -= 1; + if is_key_down(Key_Code.Up) do v[1] += 1; + if is_key_down(Key_Code.Down) do v[1] -= 1; + + v = norm(v); + + pos += v * Vec2{SPEED * dt}; + } + + + gl.ClearColor(0.5, 0.7, 1.0, 1.0); + gl.Clear(gl.COLOR_BUFFER_BIT); + + gl.LoadIdentity(); + gl.Ortho(0, f64(window.width), + 0, f64(window.height), 0, 1); + + draw_rect :: proc(x, y, w, h: f32) { + gl.Begin(gl.TRIANGLES); + defer gl.End(); + + gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0); + gl.Color3f(0, 1, 0); gl.Vertex3f(x+w, y, 0); + gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0); + + gl.Color3f(0, 0, 1); gl.Vertex3f(x+w, y+h, 0); + gl.Color3f(1, 1, 0); gl.Vertex3f(x, y+h, 0); + gl.Color3f(1, 0, 0); gl.Vertex3f(x, y, 0); + } + + draw_rect(pos.x, pos.y, 50, 50); + + display_window(&window); + if ms_to_sleep := i32(16 - 1000*dt); ms_to_sleep > 0 { + win32.sleep(ms_to_sleep); + } + } +} + + +main :: proc() { + run(); +} diff --git a/examples/hello.odin b/examples/hello.odin new file mode 100644 index 000000000..96b7af0fa --- /dev/null +++ b/examples/hello.odin @@ -0,0 +1,5 @@ +import "fmt.odin"; + +main :: proc() { + fmt.println("Hellope, world!"); +} diff --git a/examples/http_test.odin b/examples/http_test.odin new file mode 100644 index 000000000..f3359b1b8 --- /dev/null +++ b/examples/http_test.odin @@ -0,0 +1,184 @@ +import "fmt.odin"; + +foreign_system_library ws2 "Ws2_32.lib" when ODIN_OS == "windows"; + + +type SOCKET uint; +const INVALID_SOCKET = ~SOCKET(0); + +type AF enum i32 { + UNSPEC = 0, // unspecified + UNIX = 1, // local to host (pipes, portals) + INET = 2, // internetwork: UDP, TCP, etc. + IMPLINK = 3, // arpanet imp addresses + PUP = 4, // pup protocols: e.g. BSP + CHAOS = 5, // mit CHAOS protocols + NS = 6, // XEROX NS protocols + ISO = 7, // ISO protocols + OSI = ISO, // OSI is ISO + ECMA = 8, // european computer manufacturers + DATAKIT = 9, // datakit protocols + CCITT = 10, // CCITT protocols, X.25 etc + SNA = 11, // IBM SNA + DECnet = 12, // DECnet + DLI = 13, // Direct data link interface + LAT = 14, // LAT + HYLINK = 15, // NSC Hyperchannel + APPLETALK = 16, // AppleTalk + ROUTE = 17, // Internal Routing Protocol + LINK = 18, // Link layer interface + XTP = 19, // eXpress Transfer Protocol (no AF) + COIP = 20, // connection-oriented IP, aka ST II + CNT = 21, // Computer Network Technology + RTIP = 22, // Help Identify RTIP packets + IPX = 23, // Novell Internet Protocol + SIP = 24, // Simple Internet Protocol + PIP = 25, // Help Identify PIP packets + MAX = 26, +}; + +const ( + SOCK_STREAM = 1; + SOCKET_ERROR = -1; + IPPROTO_TCP = 6; + AI_PASSIVE = 0x0020; + SOMAXCONN = 128; +) +const ( + SD_RECEIVE = 0; + SD_SEND = 1; + SD_BOTH = 2; +) + +const WSADESCRIPTION_LEN = 256; +const WSASYS_STATUS_LEN = 128; +type WSADATA struct #ordered { + version: i16, + high_version: i16, + + +// NOTE(bill): This is x64 ordering + max_sockets: u16, + max_udp_dg: u16, + vendor_info: ^u8, + description: [WSADESCRIPTION_LEN+1]u8, + system_status: [WSASYS_STATUS_LEN+1]u8, +} + +type addrinfo struct #ordered { + flags: i32, + family: i32, + socktype: i32, + protocol: i32, + addrlen: uint, + canonname: ^u8, + addr: ^sockaddr, + next: ^addrinfo, +} + +type sockaddr struct #ordered { + family: u16, + data: [14]u8, +} + +foreign ws2 { + proc WSAStartup (version_requested: i16, data: ^WSADATA) -> i32; + proc WSACleanup () -> i32; + proc getaddrinfo (node_name, service_name: ^u8, hints: ^addrinfo, result: ^^addrinfo) -> i32; + proc freeaddrinfo (ai: ^addrinfo); + proc socket (af, type_, protocol: i32) -> SOCKET; + proc closesocket (s: SOCKET) -> i32; + proc bind (s: SOCKET, name: ^sockaddr, name_len: i32) -> i32; + proc listen (s: SOCKET, back_log: i32) -> i32; + proc accept (s: SOCKET, addr: ^sockaddr, addr_len: i32) -> SOCKET; + proc recv (s: SOCKET, buf: ^u8, len: i32, flags: i32) -> i32; + proc send (s: SOCKET, buf: ^u8, len: i32, flags: i32) -> i32; + proc shutdown (s: SOCKET, how: i32) -> i32; + proc WSAGetLastError() -> i32; +} +proc to_c_string(s: string) -> ^u8 { + var c_str = make([]u8, len(s)+1); + copy(c_str, []u8(s)); + c_str[len(s)] = 0; + return &c_str[0]; +} + +proc run() { + var ( + wsa: WSADATA; + res: ^addrinfo = nil; + hints: addrinfo; + s, client: SOCKET; + ) + + if WSAStartup(2 | (2 << 8), &wsa) != 0 { + fmt.println("WSAStartup failed: ", WSAGetLastError()); + return; + } + defer WSACleanup(); + + hints.family = i32(AF.INET); + hints.socktype = SOCK_STREAM; + hints.protocol = IPPROTO_TCP; + hints.flags = AI_PASSIVE; + + if getaddrinfo(nil, to_c_string("8080"), &hints, &res) != 0 { + fmt.println("getaddrinfo failed: ", WSAGetLastError()); + return; + } + defer freeaddrinfo(res); + + s = socket(res.family, res.socktype, res.protocol); + if s == INVALID_SOCKET { + fmt.println("socket failed: ", WSAGetLastError()); + return; + } + defer closesocket(s); + + bind(s, res.addr, i32(res.addrlen)); + listen(s, SOMAXCONN); + + client = accept(s, nil, 0); + if client == INVALID_SOCKET { + fmt.println("socket failed: ", WSAGetLastError()); + return; + } + defer closesocket(client); + + var html = +`HTTP/1.1 200 OK +Connection: close +Content-type: text/html + + + + Demo Title + + +

Odin Server Demo

+ + +`; + + var buf: [1024]u8; + for { + var bytes = recv(client, &buf[0], i32(len(buf)), 0); + if bytes > 0 { + // fmt.println(string(buf[0.. string { + wstr_len := 0; + for (wstr+wstr_len)^ != 0 { + wstr_len++; + } + len := 2*wstr_len-1; + buf := new_slice(byte, len+1); + str := slice_ptr(wstr, wstr_len+1); + + + i, j := 0, 0; + for str[j] != 0 { + match { + case str[j] < 0x80: + if i+1 > len { + return ""; + } + buf[i] = cast(byte)str[j]; i++; + j++; + case str[j] < 0x800: + if i+2 > len { + return ""; + } + buf[i] = cast(byte)(0xc0 + (str[j]>>6)); i++; + buf[i] = cast(byte)(0x80 + (str[j]&0x3f)); i++; + j++; + case 0xd800 <= str[j] && str[j] < 0xdc00: + if i+4 > len { + return ""; + } + c := cast(rune)((str[j] - 0xd800) << 10) + cast(rune)((str[j+1]) - 0xdc00) + 0x10000; + buf[i] = cast(byte)(0xf0 + (c >> 18)); i++; + buf[i] = cast(byte)(0x80 + ((c >> 12) & 0x3f)); i++; + buf[i] = cast(byte)(0x80 + ((c >> 6) & 0x3f)); i++; + buf[i] = cast(byte)(0x80 + ((c ) & 0x3f)); i++; + j += 2; + case 0xdc00 <= str[j] && str[j] < 0xe000: + return ""; + default: + if i+3 > len { + return ""; + } + buf[i] = 0xe0 + cast(byte) (str[j] >> 12); i++; + buf[i] = 0x80 + cast(byte)((str[j] >> 6) & 0x3f); i++; + buf[i] = 0x80 + cast(byte)((str[j] ) & 0x3f); i++; + j++; + } + } + + return cast(string)buf[..i]; +} + +is_whitespace :: proc(b: byte) -> bool { + match b { + case ' ', '\t', '\n', '\r', '\v', '\f': + return true; + } + return false; +} + +is_letter :: proc(b: byte) -> bool { + match { + case 'A' <= b && b <= 'Z', + 'a' <= b && b <= 'z', + '_' == b: + return true; + } + return false; +} + +is_digit :: proc(b: byte) -> bool { + match { + case '0' <= b && b <= '9': + return true; + } + return false; +} + + +trim :: proc(s: string) -> string { + return trim_right(trim_left(s)); +} + +trim_left :: proc(s: string) -> string { + start := 0; + for i in 0..s.count { + if is_whitespace(s[i]) { + start++; + } else { + break; + } + } + + return s[start..]; +} + + +trim_right :: proc(s: string) -> string { + end := s.count; + for i := end-1; i >= 0; i-- { + if is_whitespace(s[i]) { + end--; + } else { + break; + } + } + + return s[0..end]; +} + +errorf :: proc(format: string, args: ..any) { + fmt.fprintf(os.stderr, format, ..args); + os.exit(1); +} + +errorln :: proc(args: ..any) { + fmt.fprintln(os.stderr, ..args); + os.exit(1); +} + +output_filename :: proc(s: string) -> string { + ext := "metagen"; + cext := "c"; + + i := s.count-ext.count; + str := new_slice(byte, i+cext.count); + copy(str, cast([]byte)s[..i]); + str[i] = 'c'; + return cast(string)str; +} + +Tokenizer :: struct { + filename: string, + data: []byte, + curr: int, + read_curr: int, + line: int, + line_count: int, + + curr_rune: rune, + error_count: int, +} + +Token_Kind :: enum { + INVALID, + IDENT, + STRING, + EQUAL, + COMMA, + SEMICOLON, + OTHER, + EOF, +} + +Token :: struct { + kind: Token_Kind, + text: string, + line: int, + column: int, +} + +tokenizer_err :: proc(t: ^Tokenizer, msg: string, args: ..any) { + column := max(t.read_curr - t.line+1, 1); + + fmt.fprintf(os.stderr, "%s(%d:%d) Syntax error: ", t.filename, t.line_count, column); + fmt.fprintf(os.stderr, msg, ..args); + fmt.fprintln(os.stderr); + t.error_count++; + + if t.error_count > 10 { + os.exit(1); + } +} + +advance_to_next_rune :: proc(t: ^Tokenizer) { + if t.read_curr < t.data.count { + t.curr = t.read_curr; + if t.curr_rune == '\n' { + t.line = t.curr; + t.line_count++; + } + r := cast(rune)t.data[t.read_curr]; + width := 1; + if r == 0 { + tokenizer_err(t, "Illegal character NULL"); + } else if r >= 0x80 { + r, width = utf8.decode_rune(t.data[t.read_curr..]); + if r == utf8.RUNE_ERROR && width == 1 { + tokenizer_err(t, "Illegal UTF-8 encoding"); + } else if r == utf8.RUNE_BOM && t.curr > 0 { + tokenizer_err(t, "Illegal byte order mark"); + } + } + t.read_curr += width; + t.curr_rune = r; + } else { + t.curr = t.data.count; + if t.curr_rune == '\n' { + t.line = t.curr; + t.line_count++; + } + t.curr_rune = utf8.RUNE_EOF; + } +} + +skip_whitespace :: proc(t: ^Tokenizer) { + for t.curr_rune == ' ' || + t.curr_rune == '\t' || + t.curr_rune == '\n' || + t.curr_rune == '\r' { + advance_to_next_rune(t); + } +} +scan_escape :: proc(t: ^Tokenizer, quote: rune) -> bool { + advance_to_next_rune(t); + + r := t.curr_rune; + match r { + case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', quote: + advance_to_next_rune(t); + return true; + default: + if t.curr_rune < 0 { + tokenizer_err(t, "Escape sequence was not terminated"); + } else { + tokenizer_err(t, "Unknown espace sequence"); + } + return false; + } +} + +next_token :: proc(t: ^Tokenizer) -> Token { + using Token_Kind; + skip_whitespace(t); + + token := Token{ + line = t.line, + column = t.curr-t.line+1, + }; + + prev := t.curr; + curr_rune := t.curr_rune; + if is_letter(cast(byte)t.curr_rune) { + for is_letter(cast(byte)t.curr_rune) || is_digit(cast(byte)t.curr_rune) { + advance_to_next_rune(t); + } + + token.text = cast(string)t.data[prev..t.curr]; + token.kind = IDENT; + } else { + advance_to_next_rune(t); + token.text = cast(string)t.data[prev..t.curr]; + match curr_rune { + default: token.kind = OTHER; + case utf8.RUNE_EOF: token.kind = EOF; + + case '/': + if t.curr_rune != '/' { + token.kind = OTHER; + } else { + token.text = ""; + for t.curr_rune != '\n' && t.curr_rune != utf8.RUNE_EOF { + advance_to_next_rune(t); + } + if t.curr_rune == utf8.RUNE_EOF { + token.kind = EOF; + token.text = cast(string)t.data[t.curr..t.curr+1]; + return token; + } + return next_token(t); + } + + + + case '=': token.kind = EQUAL; + case ',': token.kind = COMMA; + case ';': token.kind = SEMICOLON; + + case '"': + for { + r := t.curr_rune; + if r == '\n' || r < 0 { + tokenizer_err(t, "String literal not terminated"); + break; + } + advance_to_next_rune(t); + if r == '"' { + break; + } + if r == '\\' { + scan_escape(t, '"'); + } + } + + token.text = cast(string)t.data[prev+1..t.curr-1]; + token.kind = STRING; + } + } + + return token; +} + +expect_token :: proc(t: ^Tokenizer, kind: Token_Kind) -> Token { + tok := next_token(t); + if tok.kind != kind { + tokenizer_err(t, "Expected %s, got %s", kind, tok.kind); + } + return tok; +} + + +alloc_command_line_arguments :: proc() -> []string { + arg_count: i32; + arg_list_ptr := win32.CommandLineToArgvW(win32.GetCommandLineW(), ^arg_count); + arg_list := new_slice(string, arg_count); + for _, i in arg_list { + arg_list[i] = alloc_ucs2_to_utf8((arg_list_ptr+i)^); + } + return arg_list; +} + +main :: proc() { + arg_list := alloc_command_line_arguments(); + + if arg_list.count < 2 { + errorln("Expected a .metagen file"); + return; + } + if arg_list.count != 2 { + errorln("Expected only one .metagen file"); + return; + } + + filename := arg_list[1]; + { // Is extension valid? + i := filename.count-1; + for i >= 0 { + if filename[i] == '.' { + break; + } + i--; + } + if ext := filename[i..]; ext != ".metagen" { + errorf("Expected a .metagen file, got %s\n", filename); + return; + } + } + + data, file_ok := os.read_entire_file(filename); + if !file_ok { + errorf("Unable to read file %s\n", filename); + return; + } + + tokenizer := Tokenizer{ + data = data, + filename = filename, + line_count = 1, + }; + t := ^tokenizer; + advance_to_next_rune(t); + if t.curr_rune == utf8.RUNE_BOM { + advance_to_next_rune(t); + } + + type: string; + prefix: string; + string_prefix: string; + settings_done := false; + + for !settings_done { + using Token_Kind; + token := next_token(t); + if token.kind == Token_Kind.EOF { + break; + } + if token.kind != IDENT { + tokenizer_err(t, "Expected an identifer"); + continue; + } + match token.text { + case "type", "prefix", "string": + default: + tokenizer_err(t, "Unknown setting %s", token.text); + } + + eq := expect_token(t, EQUAL); + ident := expect_token(t, IDENT); + match token.text { + case "type": type = ident.text; + case "prefix": prefix = ident.text; + case "string": string_prefix = ident.text; + } + + expect_token(t, SEMICOLON); + if type != "" && prefix != "" && string_prefix != "" { + settings_done = true; + } + } + + if !settings_done { + errorln("Incomplete metagen settings"); + return; + } + + + new_filename := output_filename(filename); + + file, file_err := os.open(new_filename, os.O_CREAT|os.O_TRUNC, 0); + if file_err != os.ERROR_NONE { + errorf("Unable to create file %s\n", new_filename); + return; + } + defer os.close(file); + + match type { + case "enum": + Meta_Enum :: struct { + name: string, + comment: string, + } + enums: [dynamic]Meta_Enum; + + for { + using Token_Kind; + ident := next_token(t); + if ident.kind == EOF { + break; + } + if ident.kind != IDENT { + tokenizer_err(t, "Expected an identifer, got %s", ident.text); + } + expect_token(t, COMMA); + comment := expect_token(t, STRING); + expect_token(t, SEMICOLON); + + match ident.text { + case "Kind", "COUNT": + tokenizer_err(t, "A tag cannot be called %s", ident.text); + continue; + } + + append(enums, Meta_Enum{name = ident.text, comment = comment.text}); + } + + if t.error_count > 0 { + return; + } + + fmt.fprintf(file, "typedef enum %sKind %sKind;\n", prefix, prefix); + fmt.fprintf(file, "enum %sKind {\n", prefix); + for e in enums { + fmt.fprintf(file, "\t%s_%s,\n", prefix, e.name); + } + fmt.fprintf(file, "\t%s_COUNT\n", prefix); + fmt.fprintf(file, "};\n"); + + fmt.fprintf(file, "String const %s_strings[] = {\n", string_prefix); + for e, i in enums { + fmt.fprintf(file, "\t{\"%s\", %d}", e.comment, e.comment.count); + if i+1 < enums.count { + fmt.fprint(file, ","); + } + fmt.fprintln(file, ); + } + fmt.fprintf(file, "};\n\n"); + + case "union": + Meta_Union :: struct { + name: string, + comment: string, + type: string, + } + + unions: [dynamic]Meta_Union; + + for { + using Token_Kind; + ident := next_token(t); + if ident.kind == EOF { + break; + } + if ident.kind != IDENT { + tokenizer_err(t, "Expected an identifer, got %s", ident.text); + } + expect_token(t, COMMA); + comment_string := expect_token(t, STRING); + expect_token(t, COMMA); + + brace_level := 0; + start := next_token(t); + curr := start; + ok := true; + for ok && (curr.kind != SEMICOLON || brace_level > 0) { + curr = next_token(t); + match curr.kind { + case EOF: + ok = false; + case OTHER: + match curr.text { + case "{": brace_level++; + case "}": brace_level--; + } + } + } + + name := ident.text; + + if name == "" { + continue; + } + + + if name == "Kind" { + tokenizer_err(t, "A tag cannot be called Kind"); + continue; + } + + comment := comment_string.text; + if comment != "" && comment[0] == '_' { + comment = ""; + } + + type := start.text; + type.count = curr.text.data - type.data; + type = trim(type); + + append(unions, Meta_Union{name = name, comment = comment, type = type}); + } + + if t.error_count > 0 { + return; + } + + fmt.fprintf(file, "typedef enum %sKind %sKind;\n", prefix, prefix); + fmt.fprintf(file, "enum %sKind {\n", prefix); + for u in unions { + if u.name[0] != '_' { + fmt.fprintf(file, "\t"); + } + fmt.fprintf(file, "%s_%s,\n", prefix, u.name); + } + fmt.fprintf(file, "\t%s_COUNT\n", prefix); + fmt.fprintf(file, "};\n"); + + fmt.fprintf(file, "String const %s_strings[] = {\n", string_prefix); + for u, i in unions { + fmt.fprintf(file, "\t{\"%s\", %d}", u.comment, u.comment.count); + if i+1 < unions.count { + fmt.fprint(file, ","); + } + fmt.fprintln(file, ); + } + fmt.fprintf(file, "};\n\n"); + + + for u, i in unions { + fmt.fprintf(file, "typedef %s %s%s;\n", u.type, prefix, u.name); + } + + fmt.fprintf(file, "\n\n"); + fmt.fprintf(file, "struct %s{\n", prefix); + fmt.fprintf(file, "\t%sKind kind;\n", prefix); + fmt.fprintf(file, "\tunion {\n",); + for u, i in unions { + fmt.fprintf(file, "\t\t%s%s %s;\n", prefix, u.name, u.name); + } + fmt.fprintf(file, "\t};\n"); + fmt.fprintf(file, "};\n\n"); + + fmt.fprintf(file, +` +#define %s(n_, Kind_, node_) GB_JOIN2(%s, Kind_) *n_ = &(node_)->Kind_; GB_ASSERT((node_)->kind == GB_JOIN2(%s_, Kind_)) +#define case_%s(n_, Kind_, node_) case GB_JOIN2(%s_, Kind_): { %s(n_, Kind_, node_); +#ifndef case_end +#define case_end } break; +#endif +`, + prefix, prefix, prefix, prefix, prefix); + + fmt.fprintf(file, "\n\n"); + + return; + + default: + errorf("%s is not a valid type for metagen\n", type); + return; + } + +} diff --git a/examples/old_demos/demo001.odin b/examples/old_demos/demo001.odin new file mode 100644 index 000000000..5f30af8fe --- /dev/null +++ b/examples/old_demos/demo001.odin @@ -0,0 +1,337 @@ +#import "fmt.odin"; +#import "os.odin"; +#import "mem.odin"; +// #import "http_test.odin" as ht; +// #import "game.odin" as game; +// #import "punity.odin" as pn; + +main :: proc() { + struct_padding(); + bounds_checking(); + type_introspection(); + any_type(); + crazy_introspection(); + namespaces_and_files(); + miscellany(); + + /* + ht.run(); + game.run(); + { + init :: proc(c: ^pn.Core) {} + step :: proc(c: ^pn.Core) {} + + pn.run(init, step); + } + */ +} + +struct_padding :: proc() { + { + A :: struct { + a: u8, + b: u32, + c: u16, + } + + B :: struct { + a: [7]u8, + b: [3]u16, + c: u8, + d: u16, + } + + fmt.println("size_of(A):", size_of(A)); + fmt.println("size_of(B):", size_of(B)); + + // n.b. http://cbloomrants.blogspot.co.uk/2012/07/07-23-12-structs-are-not-what-you-want.html + } + { + A :: struct #ordered { + a: u8, + b: u32, + c: u16, + } + + B :: struct #ordered { + a: [7]u8, + b: [3]u16, + c: u8, + d: u16, + } + + fmt.println("size_of(A):", size_of(A)); + fmt.println("size_of(B):", size_of(B)); + + // C-style structure layout + } + { + A :: struct #packed { + a: u8, + b: u32, + c: u16, + } + + B :: struct #packed { + a: [7]u8, + b: [3]u16, + c: u8, + d: u16, + } + + fmt.println("size_of(A):", size_of(A)); + fmt.println("size_of(B):", size_of(B)); + + // Useful for explicit layout + } + + // Member sorting by priority + // Alignment desc. + // Size desc. + // source order asc. + + /* + A :: struct { + a: u8 + b: u32 + c: u16 + } + + B :: struct { + a: [7]u8 + b: [3]u16 + c: u8 + d: u16 + } + + Equivalent too + + A :: struct #ordered { + b: u32 + c: u16 + a: u8 + } + + B :: struct #ordered { + b: [3]u16 + d: u16 + a: [7]u8 + c: u8 + } + */ +} + +bounds_checking :: proc() { + x: [4]int; + // x[-1] = 0; // Compile Time + // x[4] = 0; // Compile Time + + { + a, b := -1, 4; + // x[a] = 0; // Runtime Time + // x[b] = 0; // Runtime Time + } + + // Works for arrays, strings, slices, and related procedures & operations + + { + base: [10]int; + s := base[2..6]; + a, b := -1, 6; + + #no_bounds_check { + s[a] = 0; + // #bounds_check s[b] = 0; + } + + #no_bounds_check + if s[a] == 0 { + // Do whatever + } + + // Bounds checking can be toggled explicit + // on a per statement basis. + // _any statement_ + } +} + +type_introspection :: proc() { + { + info: ^Type_Info; + x: int; + + info = type_info(int); // by type + info = type_info_of_val(x); // by value + // See: runtime.odin + + match i in info { + case Type_Info.Integer: + fmt.println("integer!"); + case Type_Info.Float: + fmt.println("float!"); + default: + fmt.println("potato!"); + } + + // Unsafe cast + integer_info := cast(^Type_Info.Integer)cast(rawptr)info; + } + + { + Vector2 :: struct { x, y: f32 } + Vector3 :: struct { x, y, z: f32 } + + v1: Vector2; + v2: Vector3; + v3: Vector3; + + t1 := type_info_of_val(v1); + t2 := type_info_of_val(v2); + t3 := type_info_of_val(v3); + + fmt.println(); + fmt.print("Type of v1 is:\n\t", t1); + + fmt.println(); + fmt.print("Type of v2 is:\n\t", t2); + + fmt.println("\n"); + fmt.println("t1 == t2:", t1 == t2); + fmt.println("t2 == t3:", t2 == t3); + } +} + +any_type :: proc() { + a: any; + + x: int = 123; + y: f64 = 6.28; + z: string = "Yo-Yo Ma"; + // All types can be implicit cast to `any` + a = x; + a = y; + a = z; + a = a; // This the "identity" type, it doesn't get converted + + a = 123; // Literals are copied onto the stack first + + // any has two members + // data - rawptr to the data + // type_info - pointer to the type info + + fmt.println(x, y, z); + // See: fmt.odin + // For variadic any procedures in action +} + +crazy_introspection :: proc() { + { + Fruit :: enum { + APPLE, + BANANA, + GRAPE, + MELON, + PEACH, + TOMATO, + } + + s: string; + // s = enum_to_string(Fruit.PEACH); + fmt.println(s); + + f := Fruit.GRAPE; + // s = enum_to_string(f); + fmt.println(s); + + fmt.println(f); + // See: runtime.odin + } + + + { + // NOTE(bill): This is not safe code and I would not recommend this at all + // I'd recommend you use `match type` to get the subtype rather than + // casting pointers + + Fruit :: enum { + APPLE, + BANANA, + GRAPE, + MELON, + PEACH, + TOMATO, + } + + fruit_ti := type_info(Fruit); + name := (union_cast(^Type_Info.Named)fruit_ti).name; // Unsafe casts + info, _ := union_cast(^Type_Info.Enum)type_info_base(fruit_ti); // Unsafe casts + + fmt.printf("%s :: enum %T {\n", name, info.base); + for i := 0; i < len(info.values); i++ { + fmt.printf("\t%s\t= %v,\n", info.names[i], info.values[i]); + } + fmt.printf("}\n"); + + // NOTE(bill): look at that type-safe printf! + } + + { + Vector3 :: struct {x, y, z: f32} + + a := Vector3{x = 1, y = 4, z = 9}; + fmt.println(a); + b := Vector3{x = 9, y = 3, z = 1}; + fmt.println(b); + + // NOTE(bill): See fmt.odin + } + + // n.b. This pretty much "solves" serialization (to strings) +} + +// #import "test.odin" + +namespaces_and_files :: proc() { + + // test.thing() + // test.format.println() + // test.println() + /* + // Non-exporting import + #import "file.odin" + #import "file.odin" as file + #import "file.odin" as . + #import "file.odin" as _ + + // Exporting import + #include "file.odin" + */ + + // Talk about scope rules and diagram +} + +miscellany :: proc() { + /* + win32 `__imp__` prefix + #dll_import + #dll_export + + Change exported name/symbol for linking + #link_name + + Custom calling conventions + #stdcall + #fastcall + + Runtime stuff + #shared_global_scope + */ + + // assert(false) + // compile_assert(false) + // panic("Panic message goes here") +} + + + + diff --git a/examples/old_demos/demo002.odin b/examples/old_demos/demo002.odin new file mode 100644 index 000000000..78cdb194a --- /dev/null +++ b/examples/old_demos/demo002.odin @@ -0,0 +1,892 @@ +// Demo 002 +#load "fmt.odin"; +#load "math.odin"; +// #load "game.odin" + +#thread_local tls_int: int; + +main :: proc() { + // Forenotes + + // Semicolons are now optional + // Rule for when a semicolon is expected after a statement + // - If the next token is not on the same line + // - if the next token is a closing brace } + // - Otherwise, a semicolon is needed + // + // Expections: + // for, if, match + // if x := thing(); x < 123 {} + // for i := 0; i < 123; i++ {} + + // Q: Should I use the new rule or go back to the old one without optional semicolons? + + + // #thread_local - see runtime.odin and above at `tls_int` + // #foreign_system_library - see win32.odin + + // struct_compound_literals(); + // enumerations(); + // variadic_procedures(); + // new_builtins(); + // match_statement(); + // namespacing(); + // subtyping(); + // tagged_unions(); +} + +struct_compound_literals :: proc() { + Thing :: struct { + id: int, + x: f32, + name: string, + }; + { + t1: Thing; + t1.id = 1; + + t3 := Thing{}; + t4 := Thing{1, 2, "Fred"}; + // t5 := Thing{1, 2}; + + t6 := Thing{ + name = "Tom", + x = 23, + }; + } +} + +enumerations :: proc() { + { + Fruit :: enum { + APPLE, // 0 + BANANA, // 1 + PEAR, // 2 + }; + + f := Fruit.APPLE; + // g12: int = Fruit.BANANA + g: int = cast(int)Fruit.BANANA; + // However, you can use enums are index values as _any_ integer allowed + } + { + Fruit1 :: enum int { + APPLE, + BANANA, + PEAR, + } + + Fruit2 :: enum u8 { + APPLE, + BANANA, + PEAR, + } + + Fruit3 :: enum u8 { + APPLE = 1, + BANANA, // 2 + PEAR = 5, + TOMATO, // 6 + } + } + + // Q: remove the need for `type` if it's a record (struct/enum/raw_union/union)? +} + +variadic_procedures :: proc() { + print_ints :: proc(args: ..int) { + for arg, i in args { + if i > 0 { + print(", "); + } + print(arg); + } + } + + print_ints(); // nl() + print_ints(1); nl(); + print_ints(1, 2, 3); nl(); + + print_prefix_f32s :: proc(prefix: string, args: ..f32) { + print(prefix); + print(": "); + for arg, i in args { + if i > 0 { + print(", "); + } + print(arg); + } + } + + print_prefix_f32s("a"); nl(); + print_prefix_f32s("b", 1); nl(); + print_prefix_f32s("c", 1, 2, 3); nl(); + + // Internally, the variadic procedures get allocated to an array on the stack, + // and this array is passed a slice + + // This is first step for a `print` procedure but I do not have an `any` type + // yet as this requires a few other things first - i.e. introspection + + // NOTE(bill): I haven't yet added the feature of expanding a slice or array into + // a variadic a parameter but it's pretty trivial to add +} + +new_builtins :: proc() { + { + a := new(int); + b := make([]int, 12); + c := make([]int, 12, 16); + + defer free(a); + defer free(b); + defer free(c); + + // NOTE(bill): These use the current context's allocator not the default allocator + // see runtime.odin + + // Q: Should this be `free` rather than `free` and should I overload it for slices too? + + { + prev_context := context; + defer __context = prev_context; + // Q: Should I add a `push_context` feature to the language? + + __context.allocator = default_allocator(); + + a := new(int); + defer free(a); + + // Do whatever + + } + } + + { + a: int = 123; + b: type_of_val(a) = 321; + + // NOTE(bill): This matches the current naming scheme + // size_of + // align_of + // offset_of + // + // size_of_val + // align_of_val + // offset_of_val + // type_of_val + } + + { + // Compile time assert + COND :: true; + compile_assert(COND); + // compile_assert(!COND) + + // Runtime assert + x := true; + assert(x); + // assert(!x); + } + + { + x: ^u32 = nil; + y := x+100; + z := y-x; + w := slice_ptr(x, 12); + t := slice_ptr(x, 12, 16); + + // NOTE(bill): These are here because I've removed: + // pointer arithmetic + // pointer indexing + // pointer slicing + + // Reason + + a: [16]int; + a[1] = 1; + b := ^a; + // Auto pointer deref + // consistent with record members + assert(b[1] == 1); + + // Q: Should I add them back in at the cost of inconsitency? + } + + { + a, b := -1, 2; + print(min(a, b)); nl(); + print(max(a, b)); nl(); + print(abs(a)); nl(); + + // These work at compile time too + A :: -1; + B :: 2; + C :: min(A, B); + D :: max(A, B); + E :: abs(A); + + print(C); nl(); + print(D); nl(); + print(E); nl(); + } +} + + +match_statement :: proc() { + // NOTE(bill): `match` statements are similar to `switch` statements + // in other languages but there are few differences + + { + match x := 5; x { + case 1: // cases must be constant expression + print("1!\n"); + // break by default + + case 2: + s := "2!\n"; // Each case has its own scope + print(s); + break; // explicit break + + case 3, 4: // multiple cases + print("3 or 4!\n"); + + case 5: + print("5!\n"); + fallthrough; // explicit fallthrough + + default: + print("default!\n"); + } + + + + match x := 1.5; x { + case 1.5: + print("1.5!\n"); + // break by default + case TAU: + print("τ!\n"); + default: + print("default!\n"); + } + + + + match x := "Hello"; x { + case "Hello": + print("greeting\n"); + // break by default + case "Goodbye": + print("farewell\n"); + default: + print("???\n"); + } + + + + + + + a := 53; + match { + case a == 1: + print("one\n"); + case a == 2: + print("a couple\n"); + case a < 7, a == 7: + print("a few\n"); + case a < 12: // intentional bug + print("several\n"); + case a >= 12 && a < 100: + print("dozens\n"); + case a >= 100 && a < 1000: + print("hundreds\n"); + default: + print("a fuck ton\n"); + } + + // Identical to this + + b := 53; + if b == 1 { + print("one\n"); + } else if b == 2 { + print("a couple\n"); + } else if b < 7 || b == 7 { + print("a few\n"); + } else if b < 12 { // intentional bug + print("several\n"); + } else if b >= 12 && b < 100 { + print("dozens\n"); + } else if b >= 100 && b < 1000 { + print("hundreds\n"); + } else { + print("a fuck ton\n"); + } + + // However, match statements allow for `break` and `fallthrough` unlike + // an if statement + } +} + +Vector3 :: struct {x, y, z: f32} + +print_floats :: proc(args: ..f32) { + for arg, i in args { + if i > 0 { + print(", "); + } + print(arg); + } + println(); +} + +namespacing :: proc() { + { + Thing :: #type struct { + x: f32, + name: string, + }; + + a: Thing; + a.x = 3; + { + Thing :: #type struct { + y: int, + test: bool, + } + + b: Thing; // Uses this scope's Thing + b.test = true; + } + } +/* + { + Entity :: struct { + Guid :: int + Nested :: struct { + MyInt :: int + i: int + } + + CONSTANT :: 123 + + + guid: Guid + name: string + pos: Vector3 + vel: Vector3 + nested: Nested + } + + guid: Entity.Guid = Entity.CONSTANT + i: Entity.Nested.MyInt + + + + { + using Entity + guid: Guid = CONSTANT + using Nested + i: MyInt + } + + + { + using Entity.Nested + guid: Entity.Guid = Entity.CONSTANT + i: MyInt + } + + + { + e: Entity + using e + guid = 27832 + name = "Bob" + + print(e.guid as int); nl() + print(e.name); nl() + } + + { + using e: Entity + guid = 78456 + name = "Thing" + + print(e.guid as int); nl() + print(e.name); nl() + } + } + + { + Entity :: struct { + Guid :: int + Nested :: struct { + MyInt :: int + i: int + } + + CONSTANT :: 123 + + + guid: Guid + name: string + using pos: Vector3 + vel: Vector3 + using nested: ^Nested + } + + e := Entity{nested = new(Entity.Nested)} + e.x = 123 + e.i = Entity.CONSTANT + } + +*/ + + { + Entity :: struct { + position: Vector3 + } + + print_pos_1 :: proc(entity: ^Entity) { + print("print_pos_1: "); + print_floats(entity.position.x, entity.position.y, entity.position.z); + } + + print_pos_2 :: proc(entity: ^Entity) { + using entity; + print("print_pos_2: "); + print_floats(position.x, position.y, position.z); + } + + print_pos_3 :: proc(using entity: ^Entity) { + print("print_pos_3: "); + print_floats(position.x, position.y, position.z); + } + + print_pos_4 :: proc(using entity: ^Entity) { + using position; + print("print_pos_4: "); + print_floats(x, y, z); + } + + e := Entity{position = Vector3{1, 2, 3}}; + print_pos_1(^e); + print_pos_2(^e); + print_pos_3(^e); + print_pos_4(^e); + + // This is similar to C++'s `this` pointer that is implicit and only available in methods + } +} + +subtyping :: proc() { + { + // C way for subtyping/subclassing + + Entity :: struct { + position: Vector3, + } + + Frog :: struct { + entity: Entity, + jump_height: f32, + } + + f: Frog; + f.entity.position = Vector3{1, 2, 3}; + + using f.entity; + position = Vector3{1, 2, 3}; + + } + + { + // C++ way for subtyping/subclassing + + Entity :: struct { + position: Vector3 + } + + Frog :: struct { + using entity: Entity, + jump_height: f32, + } + + f: Frog; + f.position = Vector3{1, 2, 3}; + + + print_pos :: proc(using entity: Entity) { + print("print_pos: "); + print_floats(position.x, position.y, position.z); + } + + print_pos(f.entity); + // print_pos(f); + + // Subtype Polymorphism + } + + { + // More than C++ way for subtyping/subclassing + + Entity :: struct { + position: Vector3, + } + + Frog :: struct { + jump_height: f32, + using entity: ^Entity, // Doesn't have to be first member! + } + + f: Frog; + f.entity = new(Entity); + f.position = Vector3{1, 2, 3}; + + + print_pos :: proc(using entity: ^Entity) { + print("print_pos: "); + print_floats(position.x, position.y, position.z); + } + + print_pos(f.entity); + // print_pos(^f); + // print_pos(f); + } + + { + // More efficient subtyping + + Entity :: struct { + position: Vector3, + } + + Frog :: struct { + jump_height: f32, + using entity: ^Entity, + } + + MAX_ENTITES :: 64; + entities: [MAX_ENTITES]Entity; + entity_count := 0; + + next_entity :: proc(entities: []Entity, entity_count: ^int) -> ^Entity { + e := ^entities[entity_count^]; + entity_count^++; + return e; + } + + f: Frog; + f.entity = next_entity(entities[..], ^entity_count); + f.position = Vector3{3, 4, 6}; + + using f.position; + print_floats(x, y, z); + } + + { + // Down casting + + Entity :: struct { + position: Vector3, + } + + Frog :: struct { + jump_height: f32, + using entity: Entity, + } + + f: Frog; + f.jump_height = 564; + e := ^f.entity; + + frog := down_cast(^Frog)e; + print("down_cast: "); + print(frog.jump_height); nl(); + + // NOTE(bill): `down_cast` is unsafe and there are not check are compile time or run time + // Q: Should I completely remove `down_cast` as I added it in about 30 minutes + } + + { + // Multiple "inheritance"/subclassing + + Entity :: struct { + position: Vector3, + } + Climber :: struct { + speed: f32, + } + + Frog :: struct { + using entity: Entity, + using climber: Climber, + } + } +} + +tagged_unions :: proc() { + { + EntityKind :: enum { + INVALID, + FROG, + GIRAFFE, + HELICOPTER, + } + + Entity :: struct { + kind: EntityKind + using data: raw_union { + frog: struct { + jump_height: f32, + colour: u32, + }, + giraffe: struct { + neck_length: f32, + spot_count: int, + }, + helicopter: struct { + blade_count: int, + weight: f32, + pilot_name: string, + }, + } + } + + e: Entity; + e.kind = EntityKind.FROG; + e.frog.jump_height = 12; + + f: type_of_val(e.frog); + + // But this is very unsafe and extremely cumbersome to write + // In C++, I use macros to alleviate this but it's not a solution + } + + { + Entity :: union { + Frog{ + jump_height: f32, + colour: u32, + }, + Giraffe{ + neck_length: f32, + spot_count: int, + }, + Helicopter{ + blade_count: int, + weight: f32, + pilot_name: string, + }, + } + + using Entity; + f1: Frog = Frog{12, 0xff9900}; + f2: Entity = Frog{12, 0xff9900}; // Implicit cast + f3 := cast(Entity)Frog{12, 0xff9900}; // Explicit cast + + // f3.Frog.jump_height = 12 // There are "members" of a union + + + + e, f, g, h: Entity; + f = Frog{12, 0xff9900}; + g = Giraffe{2.1, 23}; + h = Helicopter{4, 1000, "Frank"}; + + + + + // Requires a pointer to the union + // `x` will be a pointer to type of the case + + match x in ^f { + case Frog: + print("Frog!\n"); + print(x.jump_height); nl(); + // x.jump_height = 3; + print(x.jump_height); nl(); + case Giraffe: + print("Giraffe!\n"); + case Helicopter: + print("ROFLCOPTER!\n"); + default: + print("invalid entity\n"); + } + + + // Q: Allow for a non pointer version with takes a copy instead? + // Or it takes the pointer the data and not a copy + + + // fp := cast(^Frog)^f; // Unsafe + // print(fp.jump_height); nl(); + + + // Internals of a tagged union + /* + struct { + data: [size_of_biggest_tag]u8, + tag_index: int, + } + */ + // This is to allow for pointer casting if needed + + + // Advantage over subtyping version + MAX_ENTITES :: 64; + entities: [MAX_ENTITES]Entity; + + entities[0] = Frog{}; + entities[1] = Helicopter{}; + // etc. + } + + + { + // Transliteration of code from this actual compiler + // Some stuff is missing + Type :: struct {}; + Scope :: struct {}; + Token :: struct {}; + AstNode :: struct {}; + ExactValue :: struct {}; + + EntityKind :: enum { + Invalid, + Constant, + Variable, + UsingVariable, + TypeName, + Procedure, + Builtin, + Count, + } + + Guid :: i64; + Entity :: struct { + + kind: EntityKind, + guid: Guid, + + scope: ^Scope, + token: Token, + type_: ^Type, + + using data: raw_union { + Constant: struct { + value: ExactValue, + }, + Variable: struct { + visited: bool, // Cycle detection + used: bool, // Variable is used + is_field: bool, // Is struct field + anonymous: bool, // Variable is an anonymous + }, + UsingVariable: struct { + }, + TypeName: struct { + }, + Procedure: struct { + used: bool, + }, + Builtin: struct { + id: int, + }, + }, + } + + // Plus all the constructing procedures that go along with them!!!! + // It's a nightmare + } + + { + Type :: struct {}; + Scope :: struct {}; + Token :: struct {}; + AstNode :: struct {}; + ExactValue :: struct {}; + + + Guid :: i64; + Entity_Base :: struct { + + } + + Entity :: union { + guid: Guid, + + scope: ^Scope, + token: Token, + type_: ^Type, + + Constant{ + value: ExactValue, + }, + Variable{ + visited: bool, // Cycle detection + used: bool, // Variable is used + is_field: bool, // Is struct field + anonymous: bool, // Variable is an anonymous + }, + UsingVariable{ + }, + TypeName{ + }, + Procedure{ + used: bool, + }, + Builtin{ + id: int, + }, + } + + using Entity; + + e: Entity; + + e = Variable{ + used = true, + anonymous = false, + }; + + + + // Q: Allow a "base" type to be added to a union? + // Or even `using` on union to get the same properties? + } + + + { + // `Raw` unions still have uses, especially for mathematic types + + Vector2 :: raw_union { + using xy_: struct { x, y: f32 }, + e: [2]f32, + v: [vector 2]f32, + } + + Vector3 :: raw_union { + using xyz_: struct { x, y, z: f32 }, + xy: Vector2, + e: [3]f32, + v: [vector 3]f32, + } + + v2: Vector2; + v2.x = 1; + v2.e[0] = 1; + v2.v[0] = 1; + + v3: Vector3; + v3.x = 1; + v3.e[0] = 1; + v3.v[0] = 1; + v3.xy.x = 1; + } +} + +nl :: proc() { println(); } diff --git a/examples/old_demos/demo004.odin b/examples/old_demos/demo004.odin new file mode 100644 index 000000000..86588d60d --- /dev/null +++ b/examples/old_demos/demo004.odin @@ -0,0 +1,66 @@ +#import "fmt.odin"; +#import "utf8.odin"; +#import "hash.odin"; +#import "mem.odin"; + +main :: proc() { + { // New Standard Library stuff + s := "Hello"; + fmt.println(s, + utf8.valid_string(s), + hash.murmur64(cast([]byte)s)); + + // utf8.odin + // hash.odin + // - crc, fnv, fnva, murmur + // mem.odin + // - Custom allocators + // - Helpers + } + + { + arena: mem.Arena; + mem.init_arena_from_context(^arena, mem.megabytes(16)); // Uses default allocator + defer mem.free_arena(^arena); + + push_allocator mem.arena_allocator(^arena) { + x := new(int); + x^ = 1337; + + fmt.println(x^); + } + + /* + push_allocator x { + ... + } + + is equivalent to: + + { + prev_allocator := __context.allocator + __context.allocator = x + defer __context.allocator = prev_allocator + + ... + } + */ + + // You can also "push" a context + + c := context; // Create copy of the allocator + c.allocator = mem.arena_allocator(^arena); + + push_context c { + x := new(int); + x^ = 365; + + fmt.println(x^); + } + } + + // Backend improvements + // - Minimal dependency building (only build what is needed) + // - Numerous bugs fixed + // - Mild parsing recovery after bad syntax error +} diff --git a/examples/old_demos/demo005.odin b/examples/old_demos/demo005.odin new file mode 100644 index 000000000..f9d1a4a4c --- /dev/null +++ b/examples/old_demos/demo005.odin @@ -0,0 +1,284 @@ +#import "fmt.odin"; +#import "utf8.odin"; +// #import "atomic.odin"; +// #import "hash.odin"; +// #import "math.odin"; +// #import "mem.odin"; +// #import "opengl.odin"; +// #import "os.odin"; +// #import "sync.odin"; +// #import win32 "sys/windows.odin"; + +main :: proc() { + // syntax(); + procedure_overloading(); +} + +syntax :: proc() { + // Cyclic type checking + // Uncomment to see the error + // A :: struct {b: B}; + // B :: struct {a: A}; + + x: int; + y := cast(f32)x; + z := transmute(u32)y; + // down_cast, union_cast are similar too + + + + // Basic directives + fmt.printf("Basic directives = %s(%d): %s\n", #file, #line, #procedure); + // NOTE: new and improved `printf` + // TODO: It does need accurate float printing + + + + // record fields use the same syntax a procedure signatures + Thing1 :: struct { + x: f32, + y: int, + z: ^[]int, + }; + Thing2 :: struct {x: f32, y: int, z: ^[]int}; + + // Slice interals are now just a `ptr+len+cap` + slice: []int; compile_assert(size_of_val(slice) == 3*size_of(int)); + + // Helper type - Help the reader understand what it is quicker + My_Int :: #type int; + My_Proc :: #type proc(int) -> f32; + + + // All declarations with : are either variable or constant + // To make these declarations syntactically consistent + v_variable := 123; + c_constant :: 123; + c_type1 :: int; + c_type2 :: []int; + c_proc :: proc() { /* code here */ }; + + +/* + x += 1; + x -= 1; + // ++ and -- have been removed + // x++; + // x--; + // Question: Should they be added again? + // They were removed as they are redundant and statements, not expressions + // like in C/C++ +*/ + + // You can now build files as a `.dll` + // `odin build_dll demo.odin` + + + // New vector syntax + u, v: [vector 3]f32; + v[0] = 123; + v.x = 123; // valid for all vectors with count 1 to 4 + + // Next part + prefixes(); +} + + +Prefix_Type :: struct {x: int, y: f32, z: rawptr}; + +#thread_local my_tls: Prefix_Type; + +prefixes :: proc() { + using var: Prefix_Type; + immutable const := Prefix_Type{1, 2, nil}; + var.x = 123; + x = 123; + // const.x = 123; // const is immutable + + + + foo :: proc(using immutable pt: Prefix_Type, immutable int_ptr: ^int) { + // int_ptr = nil; // Not valid + // int_ptr^ = 123; // Not valid + } + + + + // Same as C99's `restrict` + bar :: proc(no_alias a, b: ^int) { + // Assumes a never equals b so it can perform optimizations with that fact + } + + + when_statements(); +} + + + + + +when_statements :: proc() { + X :: 123 + 12; + Y :: X/5; + COND :: Y > 0; + + when COND { + fmt.println("Y > 0"); + } else { + fmt.println("Y <= 0"); + } + + + when false { + this_code_does_not_exist(123, 321); + but_its_syntax_is_valid(); + x :: ^^^^int; + } + + foreign_procedures(); +} + +#foreign_system_library win32_user "user32.lib" when ODIN_OS == "windows"; +// NOTE: This is done on purpose for two reasons: +// * Makes it clear where the platform specific stuff is +// * Removes the need to solve the travelling salesman problem when importing files :P + +foreign_procedures :: proc() { + ShowWindow :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #foreign win32_user; + show_window :: proc(hwnd: rawptr, cmd_show: i32) -> i32 #foreign win32_user "ShowWindow"; + // NOTE: If that library doesn't get used, it doesn't get linked with + // NOTE: There is not link checking yet to see if that procedure does come from that library + + // See sys/windows.odin for more examples + + special_expressions(); +} + +special_expressions :: proc() { +/* + // Block expression + x := { + a: f32 = 123; + b := a-123; + c := b/a; + give c; + }; // semicolon is required as it's an expression + + y := if x < 50 { + give x; + } else { + // TODO: Type cohesion is not yet finished + give 123; + }; // semicolon is required as it's an expression +*/ + + // This is allows for inline blocks of code and will be a useful feature to have when + // macros will be implemented into the language + + loops(); +} + +loops :: proc() { + // The C-style for loop + for i := 0; i < 123; i += 1 { + break; + } + for i := 0; i < 123; { + break; + } + for false { + break; + } + for { + break; + } + + for i in 0..123 { // 123 exclusive + } + + for i in 0..123-1 { // 122 inclusive + } + + for val, idx in 12..16 { + fmt.println(val, idx); + } + + primes := [..]int{2, 3, 5, 7, 11, 13, 17, 19}; + + for p in primes { + fmt.println(p); + } + + // Pointers to arrays, slices, or strings are allowed + for _ in ^primes { + // ignore the value and just iterate across it + } + + + + name := "你好,世界"; + fmt.println(name); + for r in name { + compile_assert(type_of_val(r) == rune); + fmt.printf("%r\n", r); + } + + when false { + for i, size := 0; i < name.count; i += size { + r: rune; + r, size = utf8.decode_rune(name[i..]); + fmt.printf("%r\n", r); + } + } + + procedure_overloading(); +} + + +procedure_overloading :: proc() { + THINGF :: 14451.1; + THINGI :: 14451; + + foo :: proc() { + fmt.printf("Zero args\n"); + } + foo :: proc(i: int) { + fmt.printf("int arg, i=%d\n", i); + } + foo :: proc(f: f64) { + i := cast(int)f; + fmt.printf("f64 arg, f=%d\n", i); + } + + foo(); + foo(THINGF); + // foo(THINGI); // 14451 is just a number so it could go to either procedures + foo(cast(int)THINGI); + + + + + foo :: proc(x: ^i32) -> (int, int) { + fmt.println("^int"); + return 123, cast(int)(x^); + } + foo :: proc(x: rawptr) { + fmt.println("rawptr"); + } + + + a: i32 = 123; + b: f32; + c: rawptr; + fmt.println(foo(^a)); + foo(^b); + foo(c); + // foo(nil); // nil could go to numerous types thus the ambiguity + + f: proc(); + f = foo; // The correct `foo` to chosen + f(); + + + // See math.odin and atomic.odin for more examples +} diff --git a/examples/old_demos/demo006.odin b/examples/old_demos/demo006.odin new file mode 100644 index 000000000..2f4ffef66 --- /dev/null +++ b/examples/old_demos/demo006.odin @@ -0,0 +1,318 @@ +#import "atomic.odin"; +#import "hash.odin"; +#import "mem.odin"; +#import "opengl.odin"; +#import "strconv.odin"; +#import "sync.odin"; +#import win32 "sys/windows.odin"; + +#import "fmt.odin"; +#import "os.odin"; +#import "math.odin"; + + +main :: proc() { +when true { +/* + Added: + * Unexported entities and fields using an underscore prefix + - See `sync.odin` and explain + + Removed: + * Maybe/option types + * Remove `type` keyword and other "reserved" keywords + * ..< and ... removed and replace with .. (half-closed range) + + Changed: + * `compile_assert` and `assert` return the value of the condition for semantic reasons + * thread_local -> #thread_local + * #include -> #load + * Files only get checked if they are actually used + * match x in y {} // For type match statements + * Version numbering now starts from 0.1.0 and uses the convention: + - major.minor.patch + * Core library additions to Windows specific stuff + */ + + { + Fruit :: enum { + APPLE, + BANANA, + COCONUT, + } + fmt.println(Fruit.names); + } + + { + A :: struct {x, y: f32}; + B :: struct #align 16 {x, y: f32}; + fmt.println("align_of(A) =", align_of(A)); + fmt.println("align_of(B) =", align_of(B)); + } + + { + // Removal of ..< and ... + for i in 0..16 { + } + // Is similar to + for _i := 0; _i < 16; _i++ { immutable i := _i; + } + } + + { + thing: for i in 0..10 { + for j in i+1..10 { + if j == 2 { + fmt.println(i, j); + continue thing; + } + if j == 3 { + break thing; + } + } + } + + // Works with, `for`, `for in`, `match`, `match in` + // NOTE(bill): This solves most of the problems I need `goto` for + } + + { + t := type_info(int); + using Type_Info; + match i in t { + case Integer, Float: + fmt.println("It's a number"); + } + + + x: any = 123; + foo match i in x { + case int, f32: + fmt.println("It's an int or f32"); + break foo; + } + } + + { + cond := true; + x: int; + if cond { + x = 3; + } else { + x = 4; + } + + + // Ternary operator + y := cond ? 3 : 4; + + FOO :: true ? 123 : 432; // Constant ternary expression + fmt.println("Ternary values:", y, FOO); + } + + { + // Slices now store a capacity + buf: [256]byte; + s: []byte; + s = buf[..0]; // == buf[0..0]; + fmt.println("count =", s.count); + fmt.println("capacity =", s.capacity); + append(s, 1, 2, 3); + fmt.println(s); + + s = buf[1..2..3]; + fmt.println("count =", s.count); + fmt.println("capacity =", s.capacity); + fmt.println(s); + + clear(s); // Sets count to zero + s.count = 0; // Equivalent + } + + { + Foo :: struct { + x, y, z: f32, + ok: bool, + flags: u32, + } + foo_array: [256]Foo; + foo_as_bytes: []byte = slice_to_bytes(foo_array[..]); + // Useful for things like + // os.write(handle, foo_as_bytes); + + foo_slice := slice_ptr(cast(^Foo)foo_as_bytes.data, foo_as_bytes.count/size_of(Foo), foo_as_bytes.capacity/size_of(Foo)); + // Question: Should there be a bytes_to_slice procedure or is it clearer to do this even if it is error prone? + // And if so what would the syntax be? + // slice_transmute([]Foo, foo_as_bytes); + } + + { + Vec3 :: [vector 3]f32; + + x := Vec3{1, 2, 3}; + y := Vec3{4, 5, 6}; + fmt.println(x < y); + fmt.println(x + y); + fmt.println(x - y); + fmt.println(x * y); + fmt.println(x / y); + + for i in x { + fmt.println(i); + } + + compile_assert(size_of([vector 7]bool) == size_of([7]bool)); + compile_assert(size_of([vector 7]i32) == size_of([7]i32)); + // align_of([vector 7]i32) != align_of([7]i32) // this may be the case + } + + { + // fmt.* changes + // bprint* returns `int` (bytes written) + // sprint* returns `string` (bytes written as a string) + + data: [256]byte; + str := fmt.sprintf(data[..0], "Hellope %d %s %c", 123, "others", '!'); + fmt.println(str); + + buf := data[..0]; + count := fmt.bprintf(^buf, "Hellope %d %s %c", 321, "y'all", '!'); + fmt.println(cast(string)buf[..count]); + + // NOTE(bill): We may change this but because this is a library feature, I am not that bothered yet + } + + { + x: [dynamic]f64; + reserve(x, 16); + defer free(x); // `free` is overloaded for numerous types + // Number literals can have underscores in them for readability + append(x, 2_000_000.500_000, 123, 5, 7); // variadic append + + for p, i in x { + if i > 0 { fmt.print(", "); } + fmt.print(p); + } + fmt.println(); + } + + { + // Dynamic array "literals" + x := [dynamic]f64{2_000_000.500_000, 3, 5, 7}; + defer free(x); + fmt.println(x); // fmt.print* supports printing of dynamic types + clear(x); + fmt.println(x); + } + + { + m: map[f32]int; + reserve(m, 16); + defer free(m); + + m[1.0] = 1278; + m[2.0] = 7643; + m[3.0] = 564; + _, ok := m[3.0]; + c := m[3.0]; + assert(ok && c == 564); + + fmt.print("map["); + i := 0; + for val, key in m { + if i > 0 { + fmt.print(", "); + } + fmt.printf("%v=%v", key, val); + i += 1; + } + fmt.println("]"); + } + { + m := map[string]u32{ + "a" = 56, + "b" = 13453, + "c" = 7654, + }; + defer free(m); + + c := m["c"]; + _, ok := m["c"]; + assert(ok && c == 7654); + fmt.println(m); + + delete(m, "c"); // deletes entry with key "c" + _, found := m["c"]; + assert(!found); + + fmt.println(m); + clear(m); + fmt.println(m); + + // NOTE: Fixed size maps are planned but we have not yet implemented + // them as we have had no need for them as of yet + } + + { + Vector3 :: struct{x, y, z: f32}; + Quaternion :: struct{x, y, z, w: f32}; + + Entity :: union { + // Common Fields + id: u64, + name: string, + using position: Vector3, + orientation: Quaternion, + flags: u32, + + // Variants + Frog{ + ribbit_volume: f32, + jump_height: f32, + }, + Door{ + openness: f32, + }, + Map{ + width, height: f32, + place_positions: []Vector3, + place_names: []string, + }, + } + + entity: Entity; + // implicit conversion from variant to base type + entity = Entity.Frog{ + id = 1337, + ribbit_volume = 0.5, + jump_height = 2.1, + /*other data */ + }; + + entity.name = "Frank"; + entity.position = Vector3{1, 4, 9}; + + using Entity; + match e in entity { + case Frog: + fmt.println("Ribbit"); + case Door: + fmt.println("Creak"); + case Map: + fmt.println("Rustle"); + default: + fmt.println("Just a normal entity"); + } + + if frog, ok := union_cast(Frog)entity; ok { + fmt.printf("The frog jumps %f feet high at %v\n", frog.jump_height, frog.position); + } + + // Panics if not the correct type + frog: Frog; + frog = union_cast(Frog)entity; + frog, _ = union_cast(Frog)entity; // ignore error and force cast + } +} +} + diff --git a/examples/old_demos/old_runtime.odin b/examples/old_demos/old_runtime.odin new file mode 100644 index 000000000..655058e0a --- /dev/null +++ b/examples/old_demos/old_runtime.odin @@ -0,0 +1,412 @@ +#include "win32.odin" + +assume :: proc(cond: bool) #foreign "llvm.assume" + +__debug_trap :: proc() #foreign "llvm.debugtrap" +__trap :: proc() #foreign "llvm.trap" +read_cycle_counter :: proc() -> u64 #foreign "llvm.readcyclecounter" + +bit_reverse16 :: proc(b: u16) -> u16 #foreign "llvm.bitreverse.i16" +bit_reverse32 :: proc(b: u32) -> u32 #foreign "llvm.bitreverse.i32" +bit_reverse64 :: proc(b: u64) -> u64 #foreign "llvm.bitreverse.i64" + +byte_swap16 :: proc(b: u16) -> u16 #foreign "llvm.bswap.i16" +byte_swap32 :: proc(b: u32) -> u32 #foreign "llvm.bswap.i32" +byte_swap64 :: proc(b: u64) -> u64 #foreign "llvm.bswap.i64" + +fmuladd_f32 :: proc(a, b, c: f32) -> f32 #foreign "llvm.fmuladd.f32" +fmuladd_f64 :: proc(a, b, c: f64) -> f64 #foreign "llvm.fmuladd.f64" + +// TODO(bill): make custom heap procedures +heap_alloc :: proc(len: int) -> rawptr #foreign "malloc" +heap_dealloc :: proc(ptr: rawptr) #foreign "free" + +memory_zero :: proc(data: rawptr, len: int) { + d := slice_ptr(data as ^byte, len) + for i := 0; i < len; i++ { + d[i] = 0 + } +} + +memory_compare :: proc(dst, src: rawptr, len: int) -> int { + s1, s2: ^byte = dst, src + for i := 0; i < len; i++ { + a := ptr_offset(s1, i)^ + b := ptr_offset(s2, i)^ + if a != b { + return (a - b) as int + } + } + return 0 +} + +memory_copy :: proc(dst, src: rawptr, n: int) #inline { + if dst == src { + return + } + + v128b :: type {4}u32 + compile_assert(align_of(v128b) == 16) + + d, s: ^byte = dst, src + + for ; s as uint % 16 != 0 && n != 0; n-- { + d^ = s^ + d, s = ptr_offset(d, 1), ptr_offset(s, 1) + } + + if d as uint % 16 == 0 { + for ; n >= 16; d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 { + (d as ^v128b)^ = (s as ^v128b)^ + } + + if n&8 != 0 { + (d as ^u64)^ = (s as ^u64)^ + d, s = ptr_offset(d, 8), ptr_offset(s, 8) + } + if n&4 != 0 { + (d as ^u32)^ = (s as ^u32)^; + d, s = ptr_offset(d, 4), ptr_offset(s, 4) + } + if n&2 != 0 { + (d as ^u16)^ = (s as ^u16)^ + d, s = ptr_offset(d, 2), ptr_offset(s, 2) + } + if n&1 != 0 { + d^ = s^ + d, s = ptr_offset(d, 1), ptr_offset(s, 1) + } + return; + } + + // IMPORTANT NOTE(bill): Little endian only + LS :: proc(a, b: u32) -> u32 #inline { return a << b } + RS :: proc(a, b: u32) -> u32 #inline { return a >> b } + /* NOTE(bill): Big endian version + LS :: proc(a, b: u32) -> u32 #inline { return a >> b; } + RS :: proc(a, b: u32) -> u32 #inline { return a << b; } + */ + + w, x: u32 + + if d as uint % 4 == 1 { + w = (s as ^u32)^ + d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1) + d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1) + d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1) + n -= 3 + + for n > 16 { + d32 := d as ^u32 + s32 := ptr_offset(s, 1) as ^u32 + x = s32^; d32^ = LS(w, 24) | RS(x, 8) + d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) + w = s32^; d32^ = LS(x, 24) | RS(w, 8) + d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) + x = s32^; d32^ = LS(w, 24) | RS(x, 8) + d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) + w = s32^; d32^ = LS(x, 24) | RS(w, 8) + d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) + + d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 + } + + } else if d as uint % 4 == 2 { + w = (s as ^u32)^ + d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1) + d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1) + n -= 2 + + for n > 17 { + d32 := d as ^u32 + s32 := ptr_offset(s, 2) as ^u32 + x = s32^; d32^ = LS(w, 16) | RS(x, 16) + d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) + w = s32^; d32^ = LS(x, 16) | RS(w, 16) + d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) + x = s32^; d32^ = LS(w, 16) | RS(x, 16) + d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) + w = s32^; d32^ = LS(x, 16) | RS(w, 16) + d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) + + d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 + } + + } else if d as uint % 4 == 3 { + w = (s as ^u32)^ + d^ = s^ + n -= 1 + + for n > 18 { + d32 := d as ^u32 + s32 := ptr_offset(s, 3) as ^u32 + x = s32^; d32^ = LS(w, 8) | RS(x, 24) + d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) + w = s32^; d32^ = LS(x, 8) | RS(w, 24) + d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) + x = s32^; d32^ = LS(w, 8) | RS(x, 24) + d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) + w = s32^; d32^ = LS(x, 8) | RS(w, 24) + d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) + + d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 + } + } + + if n&16 != 0 { + (d as ^v128b)^ = (s as ^v128b)^ + d, s = ptr_offset(d, 16), ptr_offset(s, 16) + } + if n&8 != 0 { + (d as ^u64)^ = (s as ^u64)^ + d, s = ptr_offset(d, 8), ptr_offset(s, 8) + } + if n&4 != 0 { + (d as ^u32)^ = (s as ^u32)^; + d, s = ptr_offset(d, 4), ptr_offset(s, 4) + } + if n&2 != 0 { + (d as ^u16)^ = (s as ^u16)^ + d, s = ptr_offset(d, 2), ptr_offset(s, 2) + } + if n&1 != 0 { + d^ = s^ + } +} + +memory_move :: proc(dst, src: rawptr, n: int) #inline { + d, s: ^byte = dst, src + if d == s { + return + } + if d >= ptr_offset(s, n) || ptr_offset(d, n) <= s { + memory_copy(d, s, n) + return + } + + // TODO(bill): Vectorize the shit out of this + if d < s { + if s as int % size_of(int) == d as int % size_of(int) { + for d as int % size_of(int) != 0 { + if n == 0 { + return + } + n-- + d^ = s^ + d, s = ptr_offset(d, 1), ptr_offset(s, 1) + } + di, si := d as ^int, s as ^int + for n >= size_of(int) { + di^ = si^ + di, si = ptr_offset(di, 1), ptr_offset(si, 1) + n -= size_of(int) + } + } + for ; n > 0; n-- { + d^ = s^ + d, s = ptr_offset(d, 1), ptr_offset(s, 1) + } + } else { + if s as int % size_of(int) == d as int % size_of(int) { + for ptr_offset(d, n) as int % size_of(int) != 0 { + if n == 0 { + return + } + n-- + d^ = s^ + d, s = ptr_offset(d, 1), ptr_offset(s, 1) + } + for n >= size_of(int) { + n -= size_of(int) + di := ptr_offset(d, n) as ^int + si := ptr_offset(s, n) as ^int + di^ = si^ + } + for ; n > 0; n-- { + d^ = s^ + d, s = ptr_offset(d, 1), ptr_offset(s, 1) + } + } + for n > 0 { + n-- + dn := ptr_offset(d, n) + sn := ptr_offset(s, n) + dn^ = sn^ + } + } +} + +__string_eq :: proc(a, b: string) -> bool { + if len(a) != len(b) { + return false + } + if ^a[0] == ^b[0] { + return true + } + return memory_compare(^a[0], ^b[0], len(a)) == 0 +} + +__string_cmp :: proc(a, b : string) -> int { + min_len := len(a) + if len(b) < min_len { + min_len = len(b) + } + for i := 0; i < min_len; i++ { + x := a[i] + y := b[i] + if x < y { + return -1 + } else if x > y { + return +1 + } + } + + if len(a) < len(b) { + return -1 + } else if len(a) > len(b) { + return +1 + } + return 0 +} + +__string_ne :: proc(a, b : string) -> bool #inline { return !__string_eq(a, b) } +__string_lt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) < 0 } +__string_gt :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > 0 } +__string_le :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) <= 0 } +__string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) >= 0 } + + + + +Allocation_Mode :: type enum { + ALLOC, + DEALLOC, + DEALLOC_ALL, + RESIZE, +} + + + +Allocator_Proc :: type proc(allocator_data: rawptr, mode: Allocation_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, flags: u64) -> rawptr + +Allocator :: type struct { + procedure: Allocator_Proc; + data: rawptr +} + + +Context :: type struct { + thread_ptr: rawptr + + user_data: rawptr + user_index: int + + allocator: Allocator +} + +#thread_local context: Context + +DEFAULT_ALIGNMENT :: 2*size_of(int) + + +__check_context :: proc() { + if context.allocator.procedure == null { + context.allocator = __default_allocator() + } + if context.thread_ptr == null { + // TODO(bill): + // context.thread_ptr = current_thread_pointer() + } +} + + +alloc :: proc(size: int) -> rawptr #inline { return alloc_align(size, DEFAULT_ALIGNMENT) } + +alloc_align :: proc(size, alignment: int) -> rawptr #inline { + __check_context() + a := context.allocator + return a.procedure(a.data, Allocation_Mode.ALLOC, size, alignment, null, 0, 0) +} + +dealloc :: proc(ptr: rawptr) #inline { + __check_context() + a := context.allocator + _ = a.procedure(a.data, Allocation_Mode.DEALLOC, 0, 0, ptr, 0, 0) +} +dealloc_all :: proc(ptr: rawptr) #inline { + __check_context() + a := context.allocator + _ = a.procedure(a.data, Allocation_Mode.DEALLOC_ALL, 0, 0, ptr, 0, 0) +} + + +resize :: proc(ptr: rawptr, old_size, new_size: int) -> rawptr #inline { return resize_align(ptr, old_size, new_size, DEFAULT_ALIGNMENT) } +resize_align :: proc(ptr: rawptr, old_size, new_size, alignment: int) -> rawptr #inline { + __check_context() + a := context.allocator + return a.procedure(a.data, Allocation_Mode.RESIZE, new_size, alignment, ptr, old_size, 0) +} + + + +default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment: int) -> rawptr { + if old_memory == null { + return alloc_align(new_size, alignment) + } + + if new_size == 0 { + dealloc(old_memory) + return null + } + + if new_size == old_size { + return old_memory + } + + new_memory := alloc_align(new_size, alignment) + if new_memory == null { + return null + } + + memory_copy(new_memory, old_memory, min(old_size, new_size)); + dealloc(old_memory) + return new_memory +} + + +__default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode, + size, alignment: int, + old_memory: rawptr, old_size: int, flags: u64) -> rawptr { + using Allocation_Mode + match mode { + case ALLOC: + return heap_alloc(size) + case RESIZE: + return default_resize_align(old_memory, old_size, size, alignment) + case DEALLOC: + heap_dealloc(old_memory) + case DEALLOC_ALL: + // NOTE(bill): Does nothing + } + + return null +} + +__default_allocator :: proc() -> Allocator { + return Allocator{ + __default_allocator_proc, + null, + } +} + + + + +__assert :: proc(msg: string) { + file_write(file_get_standard(File_Standard.ERROR), msg as []byte) + // TODO(bill): Which is better? + // __trap() + __debug_trap() +} diff --git a/examples/old_stuff/demo_backup.odin b/examples/old_stuff/demo_backup.odin new file mode 100644 index 000000000..8eeaeb357 --- /dev/null +++ b/examples/old_stuff/demo_backup.odin @@ -0,0 +1,430 @@ +import ( + "fmt.odin"; + "atomics.odin"; + "bits.odin"; + "decimal.odin"; + "hash.odin"; + "math.odin"; + "mem.odin"; + "opengl.odin"; + "os.odin"; + "raw.odin"; + "strconv.odin"; + "strings.odin"; + "sync.odin"; + "sort.odin"; + "types.odin"; + "utf8.odin"; + "utf16.odin"; +/* +*/ +) + + +general_stuff :: proc() { + // Complex numbers + a := 3 + 4i; + b: complex64 = 3 + 4i; + c: complex128 = 3 + 4i; + d := complex(2, 3); + + e := a / conj(a); + fmt.println("(3+4i)/(3-4i) =", e); + fmt.println(real(e), "+", imag(e), "i"); + + + // C-style variadic procedures + foreign __llvm_core { + // The variadic part allows for extra type checking too which C does not provide + c_printf :: proc(fmt: ^u8, #c_vararg args: ...any) -> i32 #link_name "printf" ---; + } + str := "%d\n\x00"; + // c_printf(&str[0], i32(789456123)); + + + Foo :: struct { + x: int; + y: f32; + z: string; + } + foo := Foo{123, 0.513, "A string"}; + x, y, z := expand_to_tuple(foo); + fmt.println(x, y, z); + compile_assert(type_of(x) == int); + compile_assert(type_of(y) == f32); + compile_assert(type_of(z) == string); + + + // By default, all variables are zeroed + // This can be overridden with the "uninitialized value" + // This is similar to `nil` but applied to everything + undef_int: int = ---; + + + // Context system is now implemented using Implicit Parameter Passing (IPP) + // The previous implementation was Thread Local Storage (TLS) + // IPP has the advantage that it works on systems without TLS and that you can + // link the context to the stack frame and thus look at previous contexts + // + // It does mean that a pointer is implicitly passed procedures with the default + // Odin calling convention (#cc_odin) + // This can be overridden with something like #cc_contextless or #cc_c if performance + // is worried about + +} + +foreign_blocks :: proc() { + // See sys/windows.odin +} + +default_arguments :: proc() { + hello :: proc(a: int = 9, b: int = 9) do fmt.printf("a is %d; b is %d\n", a, b); + fmt.println("\nTesting default arguments:"); + hello(1, 2); + hello(1); + hello(); +} + +named_arguments :: proc() { + Colour :: enum { + Red, + Orange, + Yellow, + Green, + Blue, + Octarine, + }; + using Colour; + + make_character :: proc(name, catch_phrase: string, favourite_colour, least_favourite_colour: Colour) { + fmt.println(); + fmt.printf("My name is %v and I like %v. %v\n", name, favourite_colour, catch_phrase); + } + + make_character("Frank", "¡Ay, caramba!", Blue, Green); + + + // As the procedures have more and more parameters, it is very easy + // to get many of the arguments in the wrong order especialy if the + // types are the same + make_character("¡Ay, caramba!", "Frank", Green, Blue); + + // Named arguments help to disambiguate this problem + make_character(catch_phrase = "¡Ay, caramba!", name = "Frank", + least_favourite_colour = Green, favourite_colour = Blue); + + + // The named arguments can be specifed in any order. + make_character(favourite_colour = Octarine, catch_phrase = "U wot m8!", + least_favourite_colour = Green, name = "Dennis"); + + + // NOTE: You cannot mix named arguments with normal values + /* + make_character("Dennis", + favourite_colour = Octarine, catch_phrase = "U wot m8!", + least_favourite_colour = Green); + */ + + + // Named arguments can also aid with default arguments + numerous_things :: proc(s: string, a := 1, b := 2, c := 3.14, + d := "The Best String!", e := false, f := 10.3/3.1, g := false) { + g_str := g ? "true" : "false"; + fmt.printf("How many?! %s: %v\n", s, g_str); + } + + numerous_things("First"); + numerous_things(s = "Second", g = true); + + + // Default values can be placed anywhere, not just at the end like in other languages + weird :: proc(pre: string, mid: int = 0, post: string) { + fmt.println(pre, mid, post); + } + + weird("How many things", 42, "huh?"); + weird(pre = "Prefix", post = "Pat"); + +} + + +default_return_values :: proc() { + foo :: proc(x: int) -> (first: string = "Hellope", second := "world!") { + match x { + case 0: return; + case 1: return "Goodbye"; + case 2: return "Goodbye", "cruel world..."; + case 3: return second = "cruel world...", first = "Goodbye"; + } + + return second = "my old friend."; + } + + fmt.printf("%s %s\n", foo(0)); + fmt.printf("%s %s\n", foo(1)); + fmt.printf("%s %s\n", foo(2)); + fmt.printf("%s %s\n", foo(3)); + fmt.printf("%s %s\n", foo(4)); + fmt.println(); + + + // A more "real" example + Error :: enum { + None, + WhyTheNumberThree, + TenIsTooBig, + }; + + Entity :: struct { + name: string; + id: u32; + } + + some_thing :: proc(input: int) -> (result: ^Entity = nil, err := Error.None) { + match { + case input == 3: return err = Error.WhyTheNumberThree; + case input >= 10: return err = Error.TenIsTooBig; + } + + e := new(Entity); + e.id = u32(input); + + return result = e; + } +} + +call_location :: proc() { + amazing :: proc(n: int, using loc := #caller_location) { + fmt.printf("%s(%d:%d) just asked to do something amazing.\n", + fully_pathed_filename, line, column); + fmt.printf("Normal -> %d\n", n); + fmt.printf("Amazing -> %d\n", n+1); + fmt.println(); + } + + loc := #location(main); + fmt.println("`main` is located at", loc); + + fmt.println("This line is located at", #location()); + fmt.println(); + + amazing(3); + amazing(4, #location(call_location)); + + // See _preload.odin for the implementations of `assert` and `panic` + +} + + +explicit_parametric_polymorphic_procedures :: proc() { + // This is how `new` is actually implemented, see _preload.odin + alloc_type :: proc(T: type) -> ^T do return cast(^T)alloc(size_of(T), align_of(T)); + + int_ptr := alloc_type(int); + defer free(int_ptr); + int_ptr^ = 137; + fmt.println(int_ptr, int_ptr^); + + // Named arguments work too! + another_ptr := alloc_type(T = f32); + defer free(another_ptr); + + + add :: proc(T: type, args: ...T) -> T { + res: T; + for arg in args do res += arg; + return res; + } + + fmt.println("add =", add(int, 1, 2, 3, 4, 5, 6)); + + swap :: proc(T: type, a, b: ^T) { + tmp := a^; + a^ = b^; + b^ = tmp; + } + + a, b: int = 3, 4; + fmt.println("Pre-swap:", a, b); + swap(int, &a, &b); + fmt.println("Post-swap:", a, b); + a, b = b, a; // Or use this syntax for this silly example case + + + Vector2 :: struct {x, y: f32;}; + { + // A more complicated example using subtyping + // Something like this could be used in a game + + Entity :: struct { + using position: Vector2; + flags: u64; + id: u64; + derived: any; + } + + Rock :: struct { + using entity: Entity; + heavy: bool; + } + Door :: struct { + using entity: Entity; + open: bool; + } + Monster :: struct { + using entity: Entity; + is_robot: bool; + is_zombie: bool; + } + + new_entity :: proc(T: type, x, y: f32) -> ^T { + result := new(T); + result.derived = result^; + result.x = x; + result.y = y; + + return result; + } + + entities: [dynamic]^Entity; + + rock := new_entity(Rock, 3, 5); + + // Named arguments work too! + door := new_entity(T = Door, x = 3, y = 6); + + // And named arguments can be any order + monster := new_entity( + y = 1, + x = 2, + T = Monster, + ); + + append(&entities, rock, door, monster); + + fmt.println("Subtyping"); + for entity in entities { + match e in entity.derived { + case Rock: fmt.println("Rock", e.x, e.y); + case Door: fmt.println("Door", e.x, e.y); + case Monster: fmt.println("Monster", e.x, e.y); + } + } + } + { + Entity :: struct { + using position: Vector2; + flags: u64; + id: u64; + variant: union { Rock, Door, Monster }; + } + + Rock :: struct { + using entity: ^Entity; + heavy: bool; + } + Door :: struct { + using entity: ^Entity; + open: bool; + } + Monster :: struct { + using entity: ^Entity; + is_robot: bool; + is_zombie: bool; + } + + new_entity :: proc(T: type, x, y: f32) -> ^T { + result := new(Entity); + result.variant = T{entity = result}; + result.x = x; + result.y = y; + + return cast(^T)&result.variant; + } + + entities: [dynamic]^Entity; + + rock := new_entity(Rock, 3, 5); + + // Named arguments work too! + door := new_entity(T = Door, x = 3, y = 6); + + // And named arguments can be any order + monster := new_entity( + y = 1, + x = 2, + T = Monster, + ); + + append(&entities, rock, door, monster); + + fmt.println("Union"); + for entity in entities { + match e in entity.variant { + case Rock: fmt.println("Rock", e.x, e.y); + case Door: fmt.println("Door", e.x, e.y); + case Monster: fmt.println("Monster", e.x, e.y); + } + } + } +} + + +implicit_polymorphic_assignment :: proc() { + yep :: proc(p: proc(x: int)) { + p(123); + } + + frank :: proc(x: $T) do fmt.println("frank ->", x); + tim :: proc(x, y: $T) do fmt.println("tim ->", x, y); + yep(frank); + // yep(tim); +} + + + + +main :: proc() { +/* + foo :: proc(x: i64, y: f32) do fmt.println("#1", x, y); + foo :: proc(x: type, y: f32) do fmt.println("#2", type_info(x), y); + foo :: proc(x: type) do fmt.println("#3", type_info(x)); + + f :: foo; + + f(y = 3785.1546, x = 123); + f(x = int, y = 897.513); + f(x = f32); + + general_stuff(); + foreign_blocks(); + default_arguments(); + named_arguments(); + default_return_values(); + call_location(); + explicit_parametric_polymorphic_procedures(); + implicit_polymorphic_assignment(); + + + // Command line argument(s)! + // -opt=0,1,2,3 +*/ +/* + program := "+ + * - /"; + accumulator := 0; + + for token in program { + match token { + case '+': accumulator += 1; + case '-': accumulator -= 1; + case '*': accumulator *= 2; + case '/': accumulator /= 2; + case: // Ignore everything else + } + } + + fmt.printf("The program \"%s\" calculates the value %d\n", + program, accumulator); +*/ +} diff --git a/examples/punity.odin b/examples/punity.odin new file mode 100644 index 000000000..33b1aecdf --- /dev/null +++ b/examples/punity.odin @@ -0,0 +1,479 @@ +import win32 "sys/windows.odin"; +import "fmt.odin"; +import "os.odin"; +import "mem.odin"; + + +CANVAS_WIDTH :: 128; +CANVAS_HEIGHT :: 128; +CANVAS_SCALE :: 3; +FRAME_TIME :: 1.0/30.0; +WINDOW_TITLE :: "Punity\x00"; + +_ :: compile_assert(CANVAS_WIDTH % 16 == 0); + + +WINDOW_WIDTH :: CANVAS_WIDTH * CANVAS_SCALE; +WINDOW_HEIGHT :: CANVAS_HEIGHT * CANVAS_SCALE; + + +STACK_CAPACITY :: 1<<20; +STORAGE_CAPACITY :: 1<<20; + +DRAW_LIST_RESERVE :: 128; + +MAX_KEYS :: 256; + +Core :: struct { + stack: ^Bank; + storage: ^Bank; + + running: bool; + key_modifiers: u32; + key_states: [MAX_KEYS]u8; + key_deltas: [MAX_KEYS]u8; + + perf_frame, + perf_frame_inner, + perf_step, + perf_audio, + perf_blit, + perf_blit_cvt, + perf_blit_gdi: Perf_Span; + + frame: i64; + + canvas: Canvas; + draw_list: ^Draw_List; +} + +Perf_Span :: struct { + stamp: f64; + delta: f32; +} + +Bank :: struct { + memory: []u8; + cursor: int; +} + +Bank_State :: struct { + state: Bank; + bank: ^Bank; +} + + +Color :: struct #raw_union { + using channels: struct{a, b, g, r: u8}; + rgba: u32; +} + +Palette :: struct { + colors: [256]Color; + colors_count: u8; +} + + +Rect :: struct #raw_union { + using minmax: struct {min_x, min_y, max_x, max_y: int}; + using pos: struct {left, top, right, bottom: int}; + e: [4]int; +} + +Bitmap :: struct { + pixels: []u8; + width: int; + height: int; +} + +Font :: struct { + using bitmap: Bitmap; + char_width: int; + char_height: int; +} + +Canvas :: struct { + using bitmap: ^Bitmap; + palette: Palette; + translate_x: int; + translate_y: int; + clip: Rect; + font: ^Font; +} + +DrawFlag :: enum { + NONE = 0, + FLIP_H = 1<<0, + FLIP_V = 1<<1, + MASK = 1<<2, +} + +Draw_Item :: struct {} +Draw_List :: struct { + items: []Draw_Item; +} + +Key :: enum { + Mod_Shift = 0x0001, + Mod_Control = 0x0002, + Mod_Alt = 0x0004, + Mod_Super = 0x0008, + + + Unknown =-1, + Invalid =-2, + + + Lbutton = 1, + Rbutton = 2, + Cancel = 3, + Mbutton = 4, + + + Back = 8, + Tab = 9, + Clear = 12, + Return = 13, + Shift = 16, + Control = 17, + Menu = 18, + Pause = 19, + Capital = 20, + Kana = 0x15, + Hangeul = 0x15, + Hangul = 0x15, + Junja = 0x17, + Final = 0x18, + Hanja = 0x19, + Kanji = 0x19, + Escape = 0x1B, + Convert = 0x1C, + Non_Convert = 0x1D, + Accept = 0x1E, + Mode_Change = 0x1F, + Space = 32, + Prior = 33, + Next = 34, + End = 35, + Home = 36, + Left = 37, + Up = 38, + Right = 39, + Down = 40, + Select = 41, + Print = 42, + Exec = 43, + Snapshot = 44, + Insert = 45, + Delete = 46, + Help = 47, + Lwin = 0x5B, + Rwin = 0x5C, + Apps = 0x5D, + Sleep = 0x5F, + Numpad0 = 0x60, + Numpad1 = 0x61, + Numpad2 = 0x62, + Numpad3 = 0x63, + Numpad4 = 0x64, + Numpad5 = 0x65, + Numpad6 = 0x66, + Numpad7 = 0x67, + Numpad8 = 0x68, + Numpad9 = 0x69, + Multiply = 0x6A, + Add = 0x6B, + Separator = 0x6C, + Subtract = 0x6D, + Decimal = 0x6E, + Divide = 0x6F, + F1 = 0x70, + F2 = 0x71, + F3 = 0x72, + F4 = 0x73, + F5 = 0x74, + F6 = 0x75, + F7 = 0x76, + F8 = 0x77, + F9 = 0x78, + F10 = 0x79, + F11 = 0x7A, + F12 = 0x7B, + F13 = 0x7C, + F14 = 0x7D, + F15 = 0x7E, + F16 = 0x7F, + F17 = 0x80, + F18 = 0x81, + F19 = 0x82, + F20 = 0x83, + F21 = 0x84, + F22 = 0x85, + F23 = 0x86, + F24 = 0x87, + Numlock = 0x90, + Scroll = 0x91, + Lshift = 0xA0, + Rshift = 0xA1, + Lcontrol = 0xA2, + Rcontrol = 0xA3, + Lmenu = 0xA4, + Rmenu = 0xA5, + + + Apostrophe = 39, /* ' */ + Comma = 44, /* , */ + Minus = 45, /* - */ + Period = 46, /* . */ + Slash = 47, /* / */ + Num0 = 48, + Num1 = 49, + Num2 = 50, + Num3 = 51, + Num4 = 52, + Num5 = 53, + Num6 = 54, + Num7 = 55, + Num8 = 56, + Num9 = 57, + Semicolon = 59, /* ; */ + Equal = 61, /* = */ + A = 65, + B = 66, + C = 67, + D = 68, + E = 69, + F = 70, + G = 71, + H = 72, + I = 73, + J = 74, + K = 75, + L = 76, + M = 77, + N = 78, + O = 79, + P = 80, + Q = 81, + R = 82, + S = 83, + T = 84, + U = 85, + V = 86, + W = 87, + X = 88, + Y = 89, + Z = 90, + Left_Bracket = 91, /* [ */ + Backslash = 92, /* \ */ + Right_Bracket = 93, /* ] */ + Grave_Accent = 96, /* ` */ +}; + + +key_down :: proc(k: Key) -> bool { + return _core.key_states[k] != 0; +} + +key_pressed :: proc(k: Key) -> bool { + return (_core.key_deltas[k] != 0) && key_down(k); +} + + + + +win32_perf_count_freq := win32.get_query_performance_frequency(); +time_now :: proc() -> f64 { + assert(win32_perf_count_freq != 0); + + counter: i64; + win32.query_performance_counter(&counter); + return f64(counter) / f64(win32_perf_count_freq); +} + +_core: Core; + +run :: proc(user_init, user_step: proc(c: ^Core)) { + using win32; + + _core.running = true; + + win32_proc :: proc(hwnd: win32.Hwnd, msg: u32, wparam: win32.Wparam, lparam: win32.Lparam) -> win32.Lresult #no_inline #cc_c { + win32_app_key_mods :: proc() -> u32 { + mods: u32 = 0; + + if is_key_down(Key_Code.Shift) do mods |= u32(Key.Mod_Shift); + if is_key_down(Key_Code.Control) do mods |= u32(Key.Mod_Control); + if is_key_down(Key_Code.Menu) do mods |= u32(Key.Mod_Alt); + if is_key_down(Key_Code.Lwin) do mods |= u32(Key.Mod_Super); + if is_key_down(Key_Code.Rwin) do mods |= u32(Key.Mod_Super); + + return mods; + } + + match msg { + case WM_KEYDOWN: + _core.key_modifiers = win32_app_key_mods(); + if wparam < MAX_KEYS { + _core.key_states[wparam] = 1; + _core.key_deltas[wparam] = 1; + } + return 0; + + case WM_KEYUP: + _core.key_modifiers = win32_app_key_mods(); + if wparam < MAX_KEYS { + _core.key_states[wparam] = 0; + _core.key_deltas[wparam] = 1; + } + return 0; + + case WM_CLOSE: + post_quit_message(0); + _core.running = false; + return 0; + } + + return def_window_proc_a(hwnd, msg, wparam, lparam); + } + + + class_name := "Punity\x00"; + window_class := Wnd_Class_Ex_A{ + class_name = &class_name[0], + size = size_of(Wnd_Class_Ex_A), + style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC, + instance = Hinstance(get_module_handle_a(nil)), + wnd_proc = win32_proc, + background = Hbrush(get_stock_object(BLACK_BRUSH)), + }; + + if register_class_ex_a(&window_class) == 0 { + fmt.fprintln(os.stderr, "register_class_ex_a failed"); + return; + } + + screen_width := get_system_metrics(SM_CXSCREEN); + screen_height := get_system_metrics(SM_CYSCREEN); + + rc: Rect; + rc.left = (screen_width - WINDOW_WIDTH) / 2; + rc.top = (screen_height - WINDOW_HEIGHT) / 2; + rc.right = rc.left + WINDOW_WIDTH; + rc.bottom = rc.top + WINDOW_HEIGHT; + + style: u32 = WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + assert(adjust_window_rect(&rc, style, 0) != 0); + + wt := WINDOW_TITLE; + + win32_window := create_window_ex_a(0, + window_class.class_name, + &wt[0], + style, + rc.left, rc.top, + rc.right-rc.left, rc.bottom-rc.top, + nil, nil, window_class.instance, + nil); + + if win32_window == nil { + fmt.fprintln(os.stderr, "create_window_ex_a failed"); + return; + } + + + window_bmi: Bitmap_Info; + window_bmi.size = size_of(Bitmap_Info_Header); + window_bmi.width = CANVAS_WIDTH; + window_bmi.height = CANVAS_HEIGHT; + window_bmi.planes = 1; + window_bmi.bit_count = 32; + window_bmi.compression = BI_RGB; + + + user_init(&_core); + + show_window(win32_window, SW_SHOW); + + window_buffer := make([]u32, CANVAS_WIDTH * CANVAS_HEIGHT); + defer free(window_buffer); + + for _, i in window_buffer do window_buffer[i] = 0xff00ff; + + dt: f64; + prev_time := time_now(); + curr_time := time_now(); + total_time: f64 = 0; + offset_x := 0; + offset_y := 0; + + message: Msg; + for _core.running { + curr_time = time_now(); + dt = curr_time - prev_time; + prev_time = curr_time; + total_time += dt; + + offset_x += 1; + offset_y += 2; + + { + buf: [128]u8; + s := fmt.bprintf(buf[..], "Punity: %.4f ms\x00", dt*1000); + win32.set_window_text_a(win32_window, &s[0]); + } + + + for y in 0..CANVAS_HEIGHT { + for x in 0..CANVAS_WIDTH { + g := (x % 32) * 8; + b := (y % 32) * 8; + window_buffer[x + y*CANVAS_WIDTH] = u32(g << 8 | b); + } + } + + mem.zero(&_core.key_deltas[0], size_of(_core.key_deltas)); + + for peek_message_a(&message, nil, 0, 0, PM_REMOVE) != 0 { + if message.message == WM_QUIT { + _core.running = false; + } + translate_message(&message); + dispatch_message_a(&message); + } + + user_step(&_core); + + dc := get_dc(win32_window); + stretch_dibits(dc, + 0, 0, CANVAS_WIDTH * CANVAS_SCALE, CANVAS_HEIGHT * CANVAS_SCALE, + 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT, + &window_buffer[0], + &window_bmi, + DIB_RGB_COLORS, + SRCCOPY); + release_dc(win32_window, dc); + + + + delta := time_now() - prev_time; + if ms := i32((FRAME_TIME - delta) * 1000); ms > 0 { + win32.sleep(ms); + } + + _core.frame += 1; + } +} + + +main :: proc() { + user_init :: proc(c: ^Core) { + + } + + user_step :: proc(c: ^Core) { + + } + + run(user_init, user_step); +} diff --git a/src/checker.cpp b/src/checker.cpp index 332a4ac19..2c90b9e80 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2251,9 +2251,8 @@ Array generate_import_dependency_graph(Checker *c, Mapdelayed_imports[i].decl; GB_ASSERT(parent->is_file); - if (decl->kind == AstNode_ImportDecl) { - ast_node(id, ImportDecl, decl); - + switch (decl->kind) { + case_ast_node(id, ImportDecl, decl); String path = id->fullpath; HashKey key = hash_string(path); Scope **found = map_get(file_scopes, key); @@ -2287,9 +2286,9 @@ Array generate_import_dependency_graph(Checker *c, Mapsucc, n); ptr_set_add(&m->scope->imported, n->scope); } - } else if (decl->kind == AstNode_ExportDecl) { - ast_node(ed, ExportDecl, decl); + case_end; + case_ast_node(ed, ExportDecl, decl); String path = ed->fullpath; HashKey key = hash_string(path); Scope **found = map_get(file_scopes, key); @@ -2321,6 +2320,7 @@ Array generate_import_dependency_graph(Checker *c, Mappred, m); import_graph_node_set_add(&m->succ, n); ptr_set_add(&m->scope->imported, n->scope); + case_end; } } @@ -2461,8 +2461,8 @@ void check_import_entities(Checker *c, Map *file_scopes) { for_array(i, node->decls) { AstNode *decl = node->decls[i]; - if (decl->kind == AstNode_ImportDecl) { - ast_node(id, ImportDecl, decl); + switch (decl->kind) { + case_ast_node(id, ImportDecl, decl); Token token = id->relpath; GB_ASSERT(parent_scope->is_file); @@ -2497,8 +2497,10 @@ void check_import_entities(Checker *c, Map *file_scopes) { } } - if (ptr_set_add(&parent_scope->imported, scope)) { - // warning(token, "Multiple import of the same file within this scope"); + if (ptr_set_exists(&parent_scope->imported, scope)) { + // error(token, "Multiple import of the same file within this scope"); + } else { + ptr_set_add(&parent_scope->imported, scope); } scope->has_been_imported = true; @@ -2515,14 +2517,10 @@ void check_import_entities(Checker *c, Map *file_scopes) { if (!is_entity_kind_exported(e->kind)) { continue; } - if (id->import_name.string == ".") { - add_entity(c, parent_scope, e->identifier, e); - } else { - if (is_entity_exported(e)) { - // TODO(bill): Should these entities be imported but cause an error when used? - bool ok = add_entity(c, parent_scope, e->identifier, e); - if (ok) map_set(&parent_scope->implicit, hash_entity(e), true); - } + if (is_entity_exported(e)) { + // TODO(bill): Should these entities be imported but cause an error when used? + bool ok = add_entity(c, parent_scope, e->identifier, e); + if (ok) map_set(&parent_scope->implicit, hash_entity(e), true); } } } @@ -2540,8 +2538,9 @@ void check_import_entities(Checker *c, Map *file_scopes) { add_entity(c, parent_scope, nullptr, e); } } - } else if (decl->kind == AstNode_ExportDecl) { - ast_node(ed, ExportDecl, decl); + case_end; + + case_ast_node(ed, ExportDecl, decl); Token token = ed->relpath; GB_ASSERT(parent_scope->is_file); @@ -2576,8 +2575,10 @@ void check_import_entities(Checker *c, Map *file_scopes) { } } - if (ptr_set_add(&parent_scope->imported, scope)) { - // warning(token, "Multiple import of the same file within this scope"); + if (ptr_set_exists(&parent_scope->imported, scope)) { + // error(token, "Multiple import of the same file within this scope"); + } else { + ptr_set_add(&parent_scope->imported, scope); } scope->has_been_imported = true; @@ -2594,6 +2595,7 @@ void check_import_entities(Checker *c, Map *file_scopes) { } } } + case_end; } } } diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp index 7f1c12c17..d87a86811 100644 --- a/src/ptr_set.cpp +++ b/src/ptr_set.cpp @@ -19,7 +19,7 @@ struct PtrSet { template void ptr_set_init (PtrSet *s, gbAllocator a, isize capacity = 16); template void ptr_set_destroy (PtrSet *s); -template bool ptr_set_add (PtrSet *s, T ptr); +template void ptr_set_add (PtrSet *s, T ptr); template bool ptr_set_exists (PtrSet *s, T ptr); template void ptr_set_remove (PtrSet *s, T ptr); template void ptr_set_clear (PtrSet *s); @@ -136,8 +136,7 @@ gb_inline bool ptr_set_exists(PtrSet *s, T ptr) { // Returns true if it already exists template -bool ptr_set_add(PtrSet *s, T ptr) { - bool exists = false; +void ptr_set_add(PtrSet *s, T ptr) { isize index; PtrSetFindResult fr; if (s->hashes.count == 0) { @@ -146,7 +145,6 @@ bool ptr_set_add(PtrSet *s, T ptr) { fr = ptr_set__find(s, ptr); if (fr.entry_index >= 0) { index = fr.entry_index; - exists = true; } else { index = ptr_set__add_entry(s, ptr); if (fr.entry_prev >= 0) { @@ -158,8 +156,6 @@ bool ptr_set_add(PtrSet *s, T ptr) { if (ptr_set__full(s)) { ptr_set_grow(s); } - - return exists; }