diff --git a/build.bat b/build.bat index 100b4bca6..aa7124ad0 100644 --- a/build.bat +++ b/build.bat @@ -45,7 +45,7 @@ pushd %build_dir% cl %compiler_settings% "..\src\main.cpp" ^ /link %linker_settings% -OUT:%exe_name% ^ - && odin run ..\examples/main.odin + && odin run ..\examples/demo001.odin :do_not_compile_exe diff --git a/examples/demo001.odin b/examples/demo001.odin new file mode 100644 index 000000000..fb08ffa8e --- /dev/null +++ b/examples/demo001.odin @@ -0,0 +1,525 @@ +#load "basic.odin" +#load "math.odin" +// #load "game.odin" + +main :: proc() { + _ = hellope(); + procedures(); + variables(); + constants(); + types(); + data_control(); + // run_game(); +} + +hellope :: proc() -> int { + print_string("Hellope, 世界\n"); + return 1; +} + +apple, banana, carrot: bool; +box, carboard: bool = true, false; +hellope_value := hellope(); + +variables :: proc() { + i: int; // initialized with zero value + j: int = 1; + x, y: int = 1, 2; + + // Type inference + apple, banana, carrot := true, 123, "carrot"; + + + // Basic Types of the Language + // + // bool + // + // i8 i16 i32 i64 i128 + // u8 u16 u32 u64 u128 + // + // f32 f64 + // + // int uint (size_of(int) = size_of(rawptr)) + // + // rawptr + // + // string + // + // byte - alias for u8 + // rune - alias for i32 // Unicode Codepoint + // + // untyped bool - "untyped" types can implicitly convert to any of the "typed" types + // untyped integer + // untyped float + // untyped pointer + // untyped string + // untyped rune + + + // // Zero values + zero_numeric := 0; + zero_boolean := false; + zero_pointer := null; + zero_string1 := ""; // Escaped string + zero_string2 := ``; // Raw string + + // Unary operators + // +a + // -a + // ~a + // !a + + // Binary operators + // a + b + // a - b + // a ~ b + // a | b + + // a * b + // a / b + // a % b + // a & b + // a &~ b == a & (~b) + // a << b + // a >> b + + // a as Type + // a transmute Type + + // a == b + // a != b + // a < b + // a > b + // a <= b + // a >= b + +} + +procedures :: proc() { + add :: proc(x: int, y: int) -> int { + return x + y; + } + print_int(add(3, 4)); // 7 + print_rune('\n'); + + add_v2 :: proc(x, y: int) -> int { + return x + y; + } + + + swap_strings :: proc(x, y: string) -> (string, string) { + return y, x; + } + a, b := swap_strings("Hellope\n", "World\n"); + print_string(a); + print_string(b); + + a, b = b, a; // Quirk of grammar the of multiple assignments + print_string(a); + print_string(b); + + // Not hints, it's mandatory + proc1 :: proc(a, b: int) #inline { + print_int(a + b); + } + proc2 :: proc(a, b: int) #no_inline { + print_int(a + b); + } +} + + +TAU :: 6.28318530718; + +constants :: proc() { + TAU :: 6.28318530718; // untyped float + WORLD_JAPANESE :: "世界"; // untyped string + + TAU_32 : f32 : 6.28318530718; + TAU_AS_32 :: 6.28318530718 as f32; +} + +nl :: proc() { print_rune('\n'); } + +types :: proc() { + + x: int = 123; + y := x; // y: int = x; + // z: f32 = x; // invalid + z: f32 = x as f32; + + ptr_z := ^z; // Pascal notation + ptr_z^ = 123; // Derefence Notation + print_f32(z); nl(); + + // ^z - pointer to z + // z^ - z from pointer + + f32_array: [12]f32; // Array of 12 f32 + f32_array[0] = 2; + f32_array[1] = 3; + // f32_array[-1] = 2; // Error - compile time check + // f32_array[13] = 2; // Error - compile time check + f32_array_len := len(f32_array); // builtin procedure + f32_array_cap := cap(f32_array); // == len(f32_array) + + api: [2]^f32; + papi: ^[2]^f32; + + f32_slice: []f32; // Array reference + f32_slice = f32_array[0:5]; + f32_slice = f32_array[:5]; + f32_slice = f32_array[:]; // f32_array[0:len(f32_array)-1]; + + f32_slice = f32_array[1:5:7]; // low:1, high:5, capacity:7 + + append_success := append(^f32_slice, 1); + _ = append(^f32_slice, 2); + + _ = copy(f32_array[0:2], f32_array[2:4]); // You can use memcpy/memmove if you want + + + s := "Hellope World"; + sub_string := s[5:10]; + + v0: {4}f32; // Vector of 4 f32 + v0[0] = 1; + v0[1] = 3; + v0[2] = 6; + v0[3] = 10; + + v1 := v0 + v0; // Simd Arithmetic + v1 = v1 - v0; + v1 *= v0; // i.e. hadamard product + v1 /= v0; + + // builtin procedure + v2 := swizzle(v0, 3, 2, 1, 0); // {10, 6, 3, 1} + + v3: {4}bool = v0 == v2; + // LLVM rant? + + + type Vec4: {4}f32; + type Array3Int: [3]int; + + type Vec3: struct { + x, y, z: f32 + } + + type BinaryNode: struct { + left, right: ^BinaryNode, // same format as procedure argument + data: rawptr, + } + + type AddProc: proc(a, b: int) -> int + + type Packed: struct #packed { + a: u8, + b: u16, + c: u32, + } + static_assert(size_of(Packed) == 7); + + + { + type MyInt: int + x: int = 1; + y: MyInt = 2; + // z := x + y; // Failure - types cannot implicit convert* + z := x as MyInt + y; // Type cast using `as` + } + + + { + // From: Quake III Arena + Q_rsqrt :: proc(number: f32) -> f32 { + i: i32; + x2, y: f32; + THREE_HALFS :: 1.5; + + x2 = number * 0.5; + y = number; + i = (^y as ^i32)^; // evil floating point bit level hacking + i = 0x5f3759df - i>>1; // what the fuck? + y = (^i as ^f32)^; + y = y * (THREE_HALFS - (x2 * y *y)); // 1st iteration + // y = y * (THREE_HALFS - (x2 * y *y)); // 2nd iteration, this can be removed + return y; + } + + Q_rsqrt_v2 :: proc(number: f32) -> f32 { + THREE_HALFS :: 1.5; + + x2 := number * 0.5; + y := number; + i := y transmute i32; // evil floating point bit level hacking + i = 0x5f3759df - i>>1; // what the fuck? + y = i transmute f32; + y = y * (THREE_HALFS - (x2 * y *y)); // 1st iteration + // y = y * (THREE_HALFS - (x2 * y *y)); // 2nd iteration, this can be removed + return y; + } + + // transmute only works if the size of the types are equal + /{ + // in C + union { + i32 i; + f32 y; + }; + }/ + } + + { // Compound Literals + a := [3]int{1, 2, 3}; + b := [3]int{}; + c := [..]int{1, 2, 3}; + + d := []int{1, 2, 3}; // slice + + e := {4}f32{1, 2, 3, 4}; + f := {4}f32{1}; // broadcasts to all + // g := {4}f32{1, 2}; // require either 1 or 4 elements + + type Vec2: {2}f32; + + h := Vec2{1, 2}; + + i := Vec2{5} * h; // For strong type safety + // FORENOTE: 5 * h was originally allowed but it was an edge case in the + // compiler I didn't think it was enough to justify have it it. + + print_f32(i[0]); print_rune(','); + print_f32(i[1]); print_rune('\n'); + } + + { + do_thing :: proc(p: proc(a, b: int) -> int) { + print_int(p(3, 4)); nl(); + } + + add :: proc(a, b: int) -> int { + return a + b; + } + + + add_lambda := proc(a, b: int) -> int { + return a - b; + }; // note semicolon + + do_thing(add); + do_thing(add_lambda); + do_thing(proc(a, b: int) -> int { + return a * b; + }); + } + + { // strings and runes + escaped := "Hellope World\n"; + raw := `Hellope World\n`; + print_string(escaped); + print_string(raw); nl(); + + // Crap shader example + shader_string := +`#version 410 + +layout (location = 0) in vec3 a_position; +layout (location = 1) in vec3 a_normal; +layout (location = 2) in vec2 a_tex_coord; + +out vec3 v_position; +out vec3 v_normal; +out vec2 v_tex_coord; + +uniform mat4 u_model_view; +uniform mat3 u_normal; +uniform mat4 u_proj; +uniform mat4 u_mvp; + +void main() { + v_tex_coord = a_tex_coord; + v_normal = normalize(u_normal * a_normal); + v_position = vec3(u_model_view * vec4(a_position, 1.0)); + + gl_Position = u_mvp * vec4(a_position, 1.0); +}`; + + + knot1 := '⌘'; + knot2 := '\u2318'; // 16 bit + knot3 := '\U00002318'; // 32 bit + knot4 := "\xe2\x8c\x98"; // Note it's a string, should I allow untyped string -> untyped rune casts? + + // String ideas "nicked" from Go, so far. I think I might change how some of it works later. + } + + + { // size, align, offset + type Thing: struct { + a: u8, + b: u16, + c, d, e: u32, + } + + s := size_of(Thing); + a := align_of(Thing); + o := offset_of(Thing, b); + + t: Thing; + + sv := size_of_val(t); + av := align_of_val(t); + ov := offset_of_val(t.b); + } +} + +data_control :: proc() { + sum := 0; + for i := 0; i < 12; i++ { + sum += 1; + } + print_string("sum = "); print_int(sum); nl(); + + sum = 1; + for ; sum < 1000000; { + sum += sum; + } + print_string("sum = "); print_int(sum); nl(); + + sum = 1; + for sum < 1000000 { + sum += sum; + } + print_string("sum = "); print_int(sum); nl(); + + // loop + // for { } == for true {} + + // Question: Should I separate all these concepts and rename it? + // + // range - iterable + // for - c style + // while + // loop - while true + + // Notes: + // conditions _must_ a boolean expression + // i++ and i-- are statements, not expressions + + + x := 2; + if x < 3 { + print_string("x < 2\n"); + } + + // Unified initializer syntax - same as for statements + if x := 2; x < 3 { + print_string("x < 2\n"); + } + + if x := 4; x < 3 { + print_string("Never called\n"); + } else { + print_string("This is called\n"); + } + + { // String comparison + a := "Hellope"; + b := "World"; + if a < b { + print_string("a < b\n"); + } + if a != b { + print_string("a != b\n"); + } + + } + + + + + { + defer print_string("日本語\n"); + print_string("Japanese\n"); + } + + { + defer print_string("1\n"); + defer print_string("2\n"); + defer print_string("3\n"); + } + + { + // C strings, yuk! + to_c_string :: proc(s: string) -> ^u8 { + c := heap_alloc(len(s)+1) as ^u8; + mem_copy(c, ^s[0], len(s)); + c[len(s)] = 0; + return c; + } + + fopen :: proc(filename, mode: ^u8) -> rawptr #foreign + fclose :: proc(f: rawptr) -> i32 #foreign + + filename := to_c_string("../examples/base.odin"); + mode := to_c_string("rb"); + defer heap_free(filename); + defer heap_free(mode); + + f := fopen(filename, mode); + if f == null { + // handle error + } + defer if f != null { + _ = fclose(f); + } + + // rest of code + + // Better version + /{ + type File: struct { filename: string } + type FileError: int + open_file :: proc(filename: string) -> (File, FileError) { ... } + close_file :: proc(f: ^File) { ... } + f, err := open_file("Test"); + if err != 0 { + // handle error + } + defer close_file(^f); + + }/ + + + } + + for i := 0; i < 100; i++ { + blah := heap_alloc(100 * size_of(int)) as ^int; + defer { + defer print_string("!"); + defer print_string("heap_free"); + heap_free(blah); + } + + if i == 3 { + // defers called + continue; + } + + if i == 5 { + // defers called + return; + } + + if i == 8 { + // defers called + break; // never happens + } + } + + defer print_string("It'll never happen, mate 1"); + print_string("It'll never happen, mate 2"); + print_string("It'll never happen, mate 3"); +} + diff --git a/examples/main.odin b/examples/game.odin similarity index 98% rename from examples/main.odin rename to examples/game.odin index 0f8662728..54076af39 100644 --- a/examples/main.odin +++ b/examples/game.odin @@ -125,8 +125,7 @@ display_window :: proc(w: ^Window) { - -main :: proc() { +run_game :: proc() { win32_proc :: proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT #no_inline { if msg == WM_DESTROY || msg == WM_CLOSE || msg == WM_QUIT { ExitProcess(0); @@ -134,9 +133,7 @@ main :: proc() { } return DefWindowProcA(hwnd, msg, wparam, lparam); } - print_f64(13.37); - print_rune('\n'); -/* + window, window_success := make_window("Odin Language Demo", 854, 480, win32_proc); if !window_success { return; @@ -189,5 +186,4 @@ main :: proc() { sleep_ms(ms_to_sleep); } } -*/ } diff --git a/src/codegen/print_llvm.cpp b/src/codegen/print_llvm.cpp index f584bbe06..0790aa7d8 100644 --- a/src/codegen/print_llvm.cpp +++ b/src/codegen/print_llvm.cpp @@ -196,10 +196,13 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) { } void ssa_print_exact_value(gbFile *f, ssaModule *m, ExactValue value, Type *type) { + type = get_base_type(type); if (is_type_float(type)) { value = exact_value_to_float(value); } else if (is_type_integer(type)) { value = exact_value_to_integer(value); + } else if (is_type_pointer(type)) { + value = exact_value_to_integer(value); } switch (value.kind) { @@ -212,7 +215,7 @@ void ssa_print_exact_value(gbFile *f, ssaModule *m, ExactValue value, Type *type ssa_fprintf(f, "\""); } break; case ExactValue_Integer: { - if (is_type_pointer(get_base_type(type))) { + if (is_type_pointer(type)) { if (value.value_integer == 0) { ssa_fprintf(f, "null"); } else { @@ -235,14 +238,14 @@ void ssa_print_exact_value(gbFile *f, ssaModule *m, ExactValue value, Type *type ssa_fprintf(f, "0x%016llx", u); } break; case ExactValue_Pointer: - if (value.value_float == NULL) { + if (value.value_pointer == NULL) { ssa_fprintf(f, "null"); } else { GB_PANIC("TODO(bill): ExactValue_Pointer"); } break; default: - GB_PANIC("Invalid ExactValue"); + GB_PANIC("Invalid ExactValue: %d", value.kind); break; } } diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index 8e83a7e09..271ce3cfb 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -908,7 +908,9 @@ ssaValue *ssa_lvalue_store(ssaProcedure *proc, ssaLvalue lval, ssaValue *value) if (lval.is_vector) { // HACK(bill): Fix how lvalues for vectors work ssaValue *v = ssa_emit_load(proc, lval.address); - ssaValue *out = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, value, lval.index)); + Type *elem_type = get_base_type(ssa_value_type(v))->vector.elem; + ssaValue *elem = ssa_emit_conv(proc, value, elem_type); + ssaValue *out = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, elem, lval.index)); return ssa_emit_store(proc, lval.address, out); } return ssa_emit_store(proc, lval.address, value); @@ -1401,22 +1403,22 @@ ssaValue *ssa_emit_conv(ssaProcedure *proc, ssaValue *value, Type *t) { return ssa_emit_load(proc, slice); } - if (is_type_vector(dst)) { - Type *dst_elem = dst->vector.elem; - value = ssa_emit_conv(proc, value, dst_elem); - ssaValue *v = ssa_add_local_generated(proc, t); - v = ssa_emit_load(proc, v); - v = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, value, v_zero32)); - // NOTE(bill): Broadcast lowest value to all values - isize index_count = dst->vector.count; - i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count); - for (isize i = 0; i < index_count; i++) { - indices[i] = 0; - } + // if (is_type_vector(dst)) { + // Type *dst_elem = dst->vector.elem; + // value = ssa_emit_conv(proc, value, dst_elem); + // ssaValue *v = ssa_add_local_generated(proc, t); + // v = ssa_emit_load(proc, v); + // v = ssa_emit(proc, ssa_make_instr_insert_element(proc, v, value, v_zero32)); + // // NOTE(bill): Broadcast lowest value to all values + // isize index_count = dst->vector.count; + // i32 *indices = gb_alloc_array(proc->module->allocator, i32, index_count); + // for (isize i = 0; i < index_count; i++) { + // indices[i] = 0; + // } - v = ssa_emit(proc, ssa_make_instr_shuffle_vector(proc, v, indices, index_count)); - return v; - } + // v = ssa_emit(proc, ssa_make_instr_shuffle_vector(proc, v, indices, index_count)); + // return v; + // } gb_printf_err("Not Identical %s != %s\n", type_to_string(src_type), type_to_string(t)); @@ -1606,10 +1608,10 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue for (AstNode *elem = cl->elem_list; elem != NULL; elem = elem->next, index++) { - ssaValue *field_expr = ssa_build_expr(proc, elem); - Type *t = ssa_value_type(field_expr); + ssaValue *field_elem = ssa_build_expr(proc, elem); + Type *t = ssa_value_type(field_elem); GB_ASSERT(t->kind != Type_Tuple); - ssaValue *ev = ssa_emit_conv(proc, field_expr, et); + ssaValue *ev = ssa_emit_conv(proc, field_elem, et); ssaValue *i = ssa_make_value_constant(proc->module->allocator, t_int, make_exact_value_integer(index)); result = ssa_emit(proc, ssa_make_instr_insert_element(proc, result, ev, i)); } @@ -1619,7 +1621,9 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue for (isize i = 0; i < index_count; i++) { indices[i] = 0; } - return ssa_emit(proc, ssa_make_instr_shuffle_vector(proc, result, indices, index_count)); + ssaValue *sv = ssa_emit(proc, ssa_make_instr_shuffle_vector(proc, result, indices, index_count)); + ssa_emit_store(proc, v, sv); + return ssa_emit_load(proc, v); } return result; @@ -1761,15 +1765,20 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue AstNode *sptr_node = ce->arg_list; AstNode *item_node = ce->arg_list->next; ssaValue *slice = ssa_build_addr(proc, sptr_node).address; - ssaValue *item_value = ssa_build_expr(proc, item_node); - Type *item_type = ssa_value_type(item_value); - ssaValue *item = ssa_add_local_generated(proc, item_type); - ssa_emit_store(proc, item, item_value); ssaValue *elem = ssa_slice_elem(proc, slice); ssaValue *len = ssa_slice_len(proc, slice); ssaValue *cap = ssa_slice_cap(proc, slice); + Type *elem_type = type_deref(get_base_type(ssa_value_type(elem))); + + ssaValue *item_value = ssa_build_expr(proc, item_node); + item_value = ssa_emit_conv(proc, item_value, elem_type); + + ssaValue *item = ssa_add_local_generated(proc, elem_type); + ssa_emit_store(proc, item, item_value); + + // NOTE(bill): Check if can append is possible Token lt = {Token_Lt}; ssaValue *cond = ssa_emit_comp(proc, lt, len, cap); @@ -1781,9 +1790,13 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue // Add new slice item ssaValue *offset = ssa_emit_ptr_offset(proc, elem, len); - i64 item_size = type_size_of(proc->module->sizes, proc->module->allocator, item_type); + i64 item_size = type_size_of(proc->module->sizes, proc->module->allocator, elem_type); ssaValue *byte_count = ssa_make_value_constant(proc->module->allocator, t_int, make_exact_value_integer(item_size)); + offset = ssa_emit_conv(proc, offset, t_rawptr); + item = ssa_emit_ptr_offset(proc, item, v_zero); + ssa_value_set_type(item, make_type_pointer(proc->module->allocator, ssa_value_type(item))); + item = ssa_emit_conv(proc, item, t_rawptr); ssa_emit(proc, ssa_make_instr_copy_memory(proc, offset, item, byte_count, 1, false)); // Increment slice length @@ -1847,6 +1860,11 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue } } + auto *pt = &proc_type_->proc.params->tuple; + for (isize i = 0; i < arg_count; i++) { + args[i] = ssa_emit_conv(proc, args[i], pt->variables[i]->type); + } + ssaValue *call = ssa_make_instr_call(proc, value, args, arg_count, type->results); return ssa_emit(proc, call); case_end; diff --git a/src/exact_value.cpp b/src/exact_value.cpp index b774d8656..f428c9834 100644 --- a/src/exact_value.cpp +++ b/src/exact_value.cpp @@ -108,6 +108,8 @@ ExactValue exact_value_to_integer(ExactValue v) { return v; case ExactValue_Float: return make_exact_value_integer(cast(i64)v.value_float); + case ExactValue_Pointer: + return make_exact_value_integer(cast(i64)cast(intptr)v.value_pointer); } ExactValue r = {ExactValue_Invalid}; return r; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 407d502f0..ceabdc645 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -371,18 +371,18 @@ void tokenizer_skip_whitespace(Tokenizer *t) { if (t->read_curr[0] == '/') { // Line comment // while (t->curr_rune != '\n') advance_to_next_rune(t); - } else if (t->read_curr[0] == '*') { // (Nested) Block comment /**/ + } else if (t->read_curr[0] == '{') { // (Nested) Block comment /{}/ isize comment_scope = 1; for (;;) { advance_to_next_rune(t); if (t->curr_rune == '/') { advance_to_next_rune(t); - if (t->curr_rune == '*') { + if (t->curr_rune == '{') { advance_to_next_rune(t); comment_scope++; } } - if (t->curr_rune == '*') { + if (t->curr_rune == '}') { advance_to_next_rune(t); if (t->curr_rune == '/') { advance_to_next_rune(t);