diff --git a/core/c/c.odin b/core/c/c.odin index a9507194f..640ef254c 100644 --- a/core/c/c.odin +++ b/core/c/c.odin @@ -1,5 +1,7 @@ package c +import "core:os" + CHAR_BIT :: 8; c_bool :: bool; @@ -12,17 +14,8 @@ c_ushort :: u16; c_int :: i32; c_uint :: u32; -when ODIN_OS == "windows" || size_of(rawptr) == 4 { - c_long :: i32; -} else { - c_long :: i64; -} - -when ODIN_OS == "windows" || size_of(rawptr) == 4 { - c_ulong :: u32; -} else { - c_ulong :: u64; -} +c_long :: (os.OS == "windows" || size_of(rawptr) == 4) ? i32 : i64; +c_ulong :: (os.OS == "windows" || size_of(rawptr) == 4) ? u32 : u64; c_longlong :: i64; c_ulonglong :: u64; diff --git a/examples/demo.odin b/examples/demo.odin deleted file mode 100644 index e0894baa5..000000000 --- a/examples/demo.odin +++ /dev/null @@ -1,753 +0,0 @@ -import "core:fmt.odin" -import "core:strconv.odin" -import "core:mem.odin" -import "core:bits.odin" -import "core:hash.odin" -import "core:math.odin" -import "core:math/rand.odin" -import "core:os.odin" -import "core:raw.odin" -import "core:sort.odin" -import "core:strings.odin" -import "core:types.odin" -import "core:utf16.odin" -import "core:utf8.odin" - -// File scope `when` statements -when ODIN_OS == "windows" { - import "core:atomics.odin" - import "core:thread.odin" - import win32 "core:sys/windows.odin" -} - -@(link_name="general_stuff") -general_stuff :: proc() { - fmt.println("# general_stuff"); - { // `do` for inline statements rather than block - foo :: proc() do fmt.println("Foo!"); - if false do foo(); - for false do foo(); - when false do foo(); - - if false do foo(); - else do foo(); - } - - { // Removal of `++` and `--` (again) - x: int; - x += 1; - x -= 1; - } - { // Casting syntaxes - i := i32(137); - ptr := &i; - - _ = (^f32)(ptr); - // ^f32(ptr) == ^(f32(ptr)) - _ = cast(^f32)ptr; - - _ = (^f32)(ptr)^; - _ = (cast(^f32)ptr)^; - - // Questions: Should there be two ways to do it? - } - - /* - * Remove *_val_of built-in procedures - * size_of, align_of, offset_of - * type_of, type_info_of - */ - - { // `expand_to_tuple` built-in procedure - Foo :: struct { - x: int, - b: bool, - } - f := Foo{137, true}; - x, b := expand_to_tuple(f); - fmt.println(f); - fmt.println(x, b); - fmt.println(expand_to_tuple(f)); - } - - { - // .. half-closed range - // ... open range - - for in 0..2 {} // 0, 1 - for in 0...2 {} // 0, 1, 2 - } - - { // Multiple sized booleans - - x0: bool; // default - x1: b8 = true; - x2: b16 = false; - x3: b32 = true; - x4: b64 = false; - - fmt.printf("x1: %T = %v;\n", x1, x1); - fmt.printf("x2: %T = %v;\n", x2, x2); - fmt.printf("x3: %T = %v;\n", x3, x3); - fmt.printf("x4: %T = %v;\n", x4, x4); - - // Having specific sized booleans is very useful when dealing with foreign code - // and to enforce specific alignment for a boolean, especially within a struct - } - - { // `distinct` types - // Originally, all type declarations would create a distinct type unless #type_alias was present. - // Now the behaviour has been reversed. All type declarations create a type alias unless `distinct` is present. - // If the type expression is `struct`, `union`, `enum`, `proc`, or `bit_field`, the types will always been distinct. - - Int32 :: i32; - #assert(Int32 == i32); - - My_Int32 :: distinct i32; - #assert(My_Int32 != i32); - - My_Struct :: struct{x: int}; - #assert(My_Struct != struct{x: int}); - } -} - - -union_type :: proc() { - fmt.println("\n# union_type"); - { - val: union{int, bool}; - val = 137; - if i, ok := val.(int); ok { - fmt.println(i); - } - val = true; - fmt.println(val); - - val = nil; - - switch v in val { - case int: fmt.println("int", v); - case bool: fmt.println("bool", v); - case: fmt.println("nil"); - } - } - { - // There is a duality between `any` and `union` - // An `any` has a pointer to the data and allows for any type (open) - // A `union` has as binary blob to store the data and allows only certain types (closed) - // The following code is with `any` but has the same syntax - val: any; - val = 137; - if i, ok := val.(int); ok { - fmt.println(i); - } - val = true; - fmt.println(val); - - val = nil; - - switch v in val { - case int: fmt.println("int", v); - case bool: fmt.println("bool", v); - case: fmt.println("nil"); - } - } - - Vector3 :: struct {x, y, z: f32}; - Quaternion :: struct {x, y, z, w: f32}; - - // More realistic examples - { - // NOTE(bill): For the above basic examples, you may not have any - // particular use for it. However, my main use for them is not for these - // simple cases. My main use is for hierarchical types. Many prefer - // subtyping, embedding the base data into the derived types. Below is - // an example of this for a basic game Entity. - - Entity :: struct { - id: u64, - name: string, - position: Vector3, - orientation: Quaternion, - - derived: any, - } - - Frog :: struct { - using entity: Entity, - jump_height: f32, - } - - Monster :: struct { - using entity: Entity, - is_robot: bool, - is_zombie: bool, - } - - // See `parametric_polymorphism` procedure for details - new_entity :: proc(T: type) -> ^Entity { - t := new(T); - t.derived = t^; - return t; - } - - entity := new_entity(Monster); - - switch e in entity.derived { - case Frog: - fmt.println("Ribbit"); - case Monster: - if e.is_robot do fmt.println("Robotic"); - if e.is_zombie do fmt.println("Grrrr!"); - } - } - - { - // NOTE(bill): A union can be used to achieve something similar. Instead - // of embedding the base data into the derived types, the derived data - // in embedded into the base type. Below is the same example of the - // basic game Entity but using an union. - - Entity :: struct { - id: u64, - name: string, - position: Vector3, - orientation: Quaternion, - - derived: union {Frog, Monster}, - } - - Frog :: struct { - using entity: ^Entity, - jump_height: f32, - } - - Monster :: struct { - using entity: ^Entity, - is_robot: bool, - is_zombie: bool, - } - - // See `parametric_polymorphism` procedure for details - new_entity :: proc(T: type) -> ^Entity { - t := new(Entity); - t.derived = T{entity = t}; - return t; - } - - entity := new_entity(Monster); - - switch e in entity.derived { - case Frog: - fmt.println("Ribbit"); - case Monster: - if e.is_robot do fmt.println("Robotic"); - if e.is_zombie do fmt.println("Grrrr!"); - } - - // NOTE(bill): As you can see, the usage code has not changed, only its - // memory layout. Both approaches have their own advantages but they can - // be used together to achieve different results. The subtyping approach - // can allow for a greater control of the memory layout and memory - // allocation, e.g. storing the derivatives together. However, this is - // also its disadvantage. You must either preallocate arrays for each - // derivative separation (which can be easily missed) or preallocate a - // bunch of "raw" memory; determining the maximum size of the derived - // types would require the aid of metaprogramming. Unions solve this - // particular problem as the data is stored with the base data. - // Therefore, it is possible to preallocate, e.g. [100]Entity. - - // It should be noted that the union approach can have the same memory - // layout as the any and with the same type restrictions by using a - // pointer type for the derivatives. - - /* - Entity :: struct { - ... - derived: union{^Frog, ^Monster}, - } - - Frog :: struct { - using entity: Entity, - ... - } - Monster :: struct { - using entity: Entity, - ... - - } - new_entity :: proc(T: type) -> ^Entity { - t := new(T); - t.derived = t; - return t; - } - */ - } -} - -parametric_polymorphism :: proc() { - fmt.println("# parametric_polymorphism"); - - print_value :: proc(value: $T) { - fmt.printf("print_value: %T %v\n", value, value); - } - - v1: int = 1; - v2: f32 = 2.1; - v3: f64 = 3.14; - v4: string = "message"; - - print_value(v1); - print_value(v2); - print_value(v3); - print_value(v4); - - fmt.println(); - - add :: proc(p, q: $T) -> T { - x: T = p + q; - return x; - } - - a := add(3, 4); - fmt.printf("a: %T = %v\n", a, a); - - b := add(3.2, 4.3); - fmt.printf("b: %T = %v\n", b, b); - - // This is how `new` is implemented - alloc_type :: proc(T: type) -> ^T { - t := cast(^T)alloc(size_of(T), align_of(T)); - t^ = T{}; // Use default initialization value - return t; - } - - copy_slice :: proc(dst, src: []$T) -> int { - n := min(len(dst), len(src)); - if n > 0 { - mem.copy(&dst[0], &src[0], n*size_of(T)); - } - return n; - } - - double_params :: proc(a: $A, b: $B) -> A { - return a + A(b); - } - - fmt.println(double_params(12, 1.345)); - - - - { // Polymorphic Types and Type Specialization - Table_Slot :: struct(Key, Value: type) { - occupied: bool, - hash: u32, - key: Key, - value: Value, - } - TABLE_SIZE_MIN :: 32; - Table :: struct(Key, Value: type) { - count: int, - allocator: Allocator, - slots: []Table_Slot(Key, Value), - } - - // Only allow types that are specializations of a (polymorphic) slice - make_slice :: proc(T: type/[]$E, len: int) -> T { - return make(T, len); - } - - - // Only allow types that are specializations of `Table` - allocate :: proc(table: ^$T/Table, capacity: int) { - c := context; - if table.allocator.procedure != nil do c.allocator = table.allocator; - - context <- c { - table.slots = make_slice(type_of(table.slots), max(capacity, TABLE_SIZE_MIN)); - } - } - - expand :: proc(table: ^$T/Table) { - c := context; - if table.allocator.procedure != nil do c.allocator = table.allocator; - - context <- c { - old_slots := table.slots; - - cap := max(2*len(table.slots), TABLE_SIZE_MIN); - allocate(table, cap); - - for s in old_slots do if s.occupied { - put(table, s.key, s.value); - } - - free(old_slots); - } - } - - // Polymorphic determination of a polymorphic struct - // put :: proc(table: ^$T/Table, key: T.Key, value: T.Value) { - put :: proc(table: ^Table($Key, $Value), key: Key, value: Value) { - hash := get_hash(key); // Ad-hoc method which would fail in a different scope - index := find_index(table, key, hash); - if index < 0 { - if f64(table.count) >= 0.75*f64(len(table.slots)) { - expand(table); - } - assert(table.count <= len(table.slots)); - - hash := get_hash(key); - index = int(hash % u32(len(table.slots))); - - for table.slots[index].occupied { - if index += 1; index >= len(table.slots) { - index = 0; - } - } - - table.count += 1; - } - - slot := &table.slots[index]; - slot.occupied = true; - slot.hash = hash; - slot.key = key; - slot.value = value; - } - - - // find :: proc(table: ^$T/Table, key: T.Key) -> (T.Value, bool) { - find :: proc(table: ^Table($Key, $Value), key: Key) -> (Value, bool) { - hash := get_hash(key); - index := find_index(table, key, hash); - if index < 0 { - return Value{}, false; - } - return table.slots[index].value, true; - } - - find_index :: proc(table: ^Table($Key, $Value), key: Key, hash: u32) -> int { - if len(table.slots) <= 0 do return -1; - - index := int(hash % u32(len(table.slots))); - for table.slots[index].occupied { - if table.slots[index].hash == hash { - if table.slots[index].key == key { - return index; - } - } - - if index += 1; index >= len(table.slots) { - index = 0; - } - } - - return -1; - } - - get_hash :: proc(s: string) -> u32 { // fnv32a - h: u32 = 0x811c9dc5; - for i in 0..len(s) { - h = (h ~ u32(s[i])) * 0x01000193; - } - return h; - } - - - table: Table(string, int); - - for i in 0..36 do put(&table, "Hellope", i); - for i in 0..42 do put(&table, "World!", i); - - found, _ := find(&table, "Hellope"); - fmt.printf("`found` is %v\n", found); - - found, _ = find(&table, "World!"); - fmt.printf("`found` is %v\n", found); - - // I would not personally design a hash table like this in production - // but this is a nice basic example - // A better approach would either use a `u64` or equivalent for the key - // and let the user specify the hashing function or make the user store - // the hashing procedure with the table - } -} - - - - -prefix_table := [?]string{ - "White", - "Red", - "Green", - "Blue", - "Octarine", - "Black", -}; - -threading_example :: proc() { - when ODIN_OS == "windows" { - fmt.println("# threading_example"); - - unordered_remove :: proc(array: ^[dynamic]$T, index: int, loc := #caller_location) { - __bounds_check_error_loc(loc, index, len(array)); - array[index] = array[len(array)-1]; - pop(array); - } - ordered_remove :: proc(array: ^[dynamic]$T, index: int, loc := #caller_location) { - __bounds_check_error_loc(loc, index, len(array)); - copy(array[index..], array[index+1..]); - pop(array); - } - - worker_proc :: proc(t: ^thread.Thread) -> int { - for iteration in 1...5 { - fmt.printf("Thread %d is on iteration %d\n", t.user_index, iteration); - fmt.printf("`%s`: iteration %d\n", prefix_table[t.user_index], iteration); - // win32.sleep(1); - } - return 0; - } - - threads := make([dynamic]^thread.Thread, 0, len(prefix_table)); - defer free(threads); - - for in prefix_table { - if t := thread.create(worker_proc); t != nil { - t.init_context = context; - t.use_init_context = true; - t.user_index = len(threads); - append(&threads, t); - thread.start(t); - } - } - - for len(threads) > 0 { - for i := 0; i < len(threads); /**/ { - if t := threads[i]; thread.is_done(t) { - fmt.printf("Thread %d is done\n", t.user_index); - thread.destroy(t); - - ordered_remove(&threads, i); - } else { - i += 1; - } - } - } - } -} - -array_programming :: proc() { - fmt.println("# array_programming"); - { - a := [3]f32{1, 2, 3}; - b := [3]f32{5, 6, 7}; - c := a * b; - d := a + b; - e := 1 + (c - d) / 2; - fmt.printf("%.1f\n", e); // [0.5, 3.0, 6.5] - } - - { - a := [3]f32{1, 2, 3}; - b := swizzle(a, 2, 1, 0); - assert(b == [3]f32{3, 2, 1}); - - c := swizzle(a, 0, 0); - assert(c == [2]f32{1, 1}); - assert(c == 1); - } - - { - Vector3 :: distinct [3]f32; - a := Vector3{1, 2, 3}; - b := Vector3{5, 6, 7}; - c := (a * b)/2 + 1; - d := c.x + c.y + c.z; - fmt.printf("%.1f\n", d); // 22.0 - - cross :: proc(a, b: Vector3) -> Vector3 { - i := swizzle(a, 1, 2, 0) * swizzle(b, 2, 0, 1); - j := swizzle(a, 2, 0, 1) * swizzle(b, 1, 2, 0); - return i - j; - } - - blah :: proc(a: Vector3) -> f32 { - return a.x + a.y + a.z; - } - - x := cross(a, b); - fmt.println(x); - fmt.println(blah(x)); - } -} - - -using println in import "core:fmt.odin" - -using_in :: proc() { - fmt.println("# using in"); - using print in fmt; - - println("Hellope1"); - print("Hellope2\n"); - - Foo :: struct { - x, y: int, - b: bool, - } - f: Foo; - f.x, f.y = 123, 321; - println(f); - using x, y in f; - x, y = 456, 654; - println(f); -} - -named_proc_return_parameters :: proc() { - fmt.println("# named proc return parameters"); - - foo0 :: proc() -> int { - return 123; - } - foo1 :: proc() -> (a: int) { - a = 123; - return; - } - foo2 :: proc() -> (a, b: int) { - // Named return values act like variables within the scope - a = 321; - b = 567; - return b, a; - } - fmt.println("foo0 =", foo0()); // 123 - fmt.println("foo1 =", foo1()); // 123 - fmt.println("foo2 =", foo2()); // 567 321 -} - - -enum_export :: proc() { - fmt.println("# enum #export"); - - Foo :: enum #export {A, B, C}; - - f0 := A; - f1 := B; - f2 := C; - fmt.println(f0, f1, f2); -} - -explicit_procedure_overloading :: proc() { - fmt.println("# explicit procedure overloading"); - - add_ints :: proc(a, b: int) -> int { - x := a + b; - fmt.println("add_ints", x); - return x; - } - add_floats :: proc(a, b: f32) -> f32 { - x := a + b; - fmt.println("add_floats", x); - return x; - } - add_numbers :: proc(a: int, b: f32, c: u8) -> int { - x := int(a) + int(b) + int(c); - fmt.println("add_numbers", x); - return x; - } - - add :: proc[add_ints, add_floats, add_numbers]; - - add(int(1), int(2)); - add(f32(1), f32(2)); - add(int(1), f32(2), u8(3)); - - add(1, 2); // untyped ints coerce to int tighter than f32 - add(1.0, 2.0); // untyped floats coerce to f32 tighter than int - add(1, 2, 3); // three parameters - - // Ambiguous answers - // add(1.0, 2); - // add(1, 2.0); -} - -complete_switch :: proc() { - fmt.println("# complete_switch"); - { // enum - Foo :: enum #export { - A, - B, - C, - D, - } - - b := Foo.B; - f := Foo.A; - #complete switch f { - case A: fmt.println("A"); - case B: fmt.println("B"); - case C: fmt.println("C"); - case D: fmt.println("D"); - case: fmt.println("?"); - } - } - { // union - Foo :: union {int, bool}; - f: Foo = 123; - #complete switch in f { - case int: fmt.println("int"); - case bool: fmt.println("bool"); - case: - } - } -} - - -cstring_example :: proc() { - W :: "Hellope"; - X :: cstring(W); - Y :: string(X); - - w := W; - x: cstring = X; - y: string = Y; - z := string(x); - fmt.println(x, y, z); - fmt.println(len(x), len(y), len(z)); - fmt.println(len(W), len(X), len(Y)); - // IMPORTANT NOTE for cstring variables - // len(cstring) is O(N) - // cast(cstring)string is O(N) -} - -deprecated_attribute :: proc() { - @(deprecated="Use foo_v2 instead") - foo_v1 :: proc(x: int) { - fmt.println("foo_v1"); - } - foo_v2 :: proc(x: int) { - fmt.println("foo_v2"); - } - - // NOTE: Uncomment to see the warning messages - // foo_v1(1); -} - - -main :: proc() { - when true { - general_stuff(); - union_type(); - parametric_polymorphism(); - threading_example(); - array_programming(); - using_in(); - named_proc_return_parameters(); - enum_export(); - explicit_procedure_overloading(); - complete_switch(); - cstring_example(); - deprecated_attribute(); - } -} diff --git a/examples/factorial.odin b/examples/factorial.odin deleted file mode 100644 index dd9b6f6eb..000000000 --- a/examples/factorial.odin +++ /dev/null @@ -1,20 +0,0 @@ -import "core: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 deleted file mode 100644 index b0f4c990c..000000000 --- a/examples/game.odin +++ /dev/null @@ -1,221 +0,0 @@ -when ODIN_OS == "windows" do import win32 "core:sys/windows.odin"; -when ODIN_OS == "windows" import wgl "core:sys/wgl.odin"; -import "core:fmt.odin"; -import "core:math.odin"; -import "core:os.odin"; -import gl "core: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 deleted file mode 100644 index 95ff21670..000000000 --- a/examples/hello.odin +++ /dev/null @@ -1,5 +0,0 @@ -import "core:fmt.odin"; - -main :: proc() { - fmt.println("Hellope, world!"); -} diff --git a/examples/punity.odin b/examples/punity.odin deleted file mode 100644 index e8b0532f2..000000000 --- a/examples/punity.odin +++ /dev/null @@ -1,479 +0,0 @@ -import win32 "core:sys/windows.odin"; -import "core:fmt.odin"; -import "core:os.odin"; -import "core:mem.odin"; - - -CANVAS_WIDTH :: 128; -CANVAS_HEIGHT :: 128; -CANVAS_SCALE :: 3; -FRAME_TIME :: 1.0/30.0; -WINDOW_TITLE :: "Punity\x00"; - -#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 f1ec0d182..92595d580 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -2532,7 +2532,7 @@ void check_add_import_decl(Checker *c, AstNodeImportDecl *id) { GB_ASSERT(scope->is_package && scope->package != nullptr); if (scope->is_global) { - error(token, "Importing a runtime package is disallowed and unnecessary"); + error(token, "Importing a built-in package is disallowed and unnecessary"); return; } @@ -2567,7 +2567,7 @@ void check_add_import_decl(Checker *c, AstNodeImportDecl *id) { if (id->is_using) { if (parent_scope->is_global) { - error(id->import_name, "'runtime' package imports cannot use using"); + error(id->import_name, "'builtin' package imports cannot use using"); return; } diff --git a/src/parser.cpp b/src/parser.cpp index 3945a9415..0bcc6be4f 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -374,6 +374,9 @@ void error(AstNode *node, char *fmt, ...) { va_start(va, fmt); error_va(token, fmt, va); va_end(va); + if (node != nullptr && node->file != nullptr) { + node->file->error_count += 1; + } } void error_no_newline(AstNode *node, char *fmt, ...) { @@ -385,6 +388,9 @@ void error_no_newline(AstNode *node, char *fmt, ...) { va_start(va, fmt); error_no_newline_va(token, fmt, va); va_end(va); + if (node != nullptr && node->file != nullptr) { + node->file->error_count += 1; + } } void warning(AstNode *node, char *fmt, ...) { @@ -399,6 +405,9 @@ void syntax_error(AstNode *node, char *fmt, ...) { va_start(va, fmt); syntax_error_va(ast_node_token(node), fmt, va); va_end(va); + if (node != nullptr && node->file != nullptr) { + node->file->error_count += 1; + } } @@ -1227,6 +1236,7 @@ void fix_advance_to_next_stmt(AstFile *f) { return; + case Token_package: case Token_foreign: case Token_import: case Token_export: @@ -4144,7 +4154,6 @@ void parse_file(Parser *p, AstFile *f) { comsume_comment_groups(f, f->prev_token); - f->package_token = expect_token(f, Token_package); Token package_name = expect_token_after(f, Token_Ident, "package"); if (package_name.kind == Token_Ident) { @@ -4156,6 +4165,10 @@ void parse_file(Parser *p, AstFile *f) { } f->package_name = package_name.string; + if (f->error_count > 0) { + return; + } + f->decls = parse_stmt_list(f); parse_setup_file_decls(p, f, base_dir, f->decls); } @@ -4218,7 +4231,6 @@ skip: parse_file(p, file); gb_mutex_lock(&p->file_add_mutex); - // file->id = imported_package.index; HashKey key = hash_string(fi->fullpath); map_set(&package->files, key, file); @@ -4332,6 +4344,14 @@ ParseFileError parse_packages(Parser *p, String init_filename) { char *fullpath_str = gb_path_get_full_name(heap_allocator(), cast(char *)&init_filename[0]); String init_fullpath = string_trim_whitespace(make_string_c(fullpath_str)); + if (!path_is_directory(init_fullpath)) { + String const ext = str_lit(".odin"); + if (!string_ends_with(init_fullpath, ext)) { + gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename)); + return ParseFile_WrongExtension; + } + init_fullpath = directory_from_path(init_fullpath); + } TokenPos init_pos = {}; ImportedPackage init_imported_package = {ImportedPackage_Init, init_fullpath, init_fullpath, init_pos}; @@ -4347,7 +4367,7 @@ ParseFileError parse_packages(Parser *p, String init_filename) { p->init_fullpath = init_fullpath; // IMPORTANT TODO(bill): Figure out why this doesn't work on *nix sometimes -#if 0 && defined(GB_SYSTEM_WINDOWS) +#if defined(GB_SYSTEM_WINDOWS) isize thread_count = gb_max(build_context.thread_count, 1); if (thread_count > 1) { isize volatile curr_import_index = 0; diff --git a/src/parser.hpp b/src/parser.hpp index 4f33f44d2..2553d4776 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -70,7 +70,7 @@ struct AstFile { AstNode * curr_proc; isize scope_level; // DeclInfo * decl_info; // NOTE(bill): Created in checker - + isize error_count; CommentGroup lead_comment; // Comment (block) before the decl CommentGroup line_comment; // Comment after the semicolon diff --git a/src/string.cpp b/src/string.cpp index 32d97184a..96e17f58f 100644 --- a/src/string.cpp +++ b/src/string.cpp @@ -287,6 +287,16 @@ String remove_directory_from_path(String const &s) { } return substring(s, s.len-len, s.len); } +String directory_from_path(String const &s) { + isize i = s.len-1; + for (; i >= 0; i--) { + if (s[i] == '/' || + s[i] == '\\') { + break; + } + } + return substring(s, 0, i); +} String concatenate_strings(gbAllocator a, String const &x, String const &y) {