diff --git a/code/demo.odin b/code/demo.odin index c673b54fb..7f941a764 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -1,34 +1,241 @@ -#import "atomic.odin"; #import "fmt.odin"; -#import "hash.odin"; -#import "math.odin"; -#import "mem.odin"; -#import "opengl.odin"; -#import "os.odin"; -#import win32 "sys/windows.odin"; -#import "sync.odin"; #import "utf8.odin"; main :: proc() { - foo :: proc(x: ^i32) -> (int, int) { - fmt.println("^int"); - return 123, cast(int)(x^); - } - foo :: proc(x: rawptr) { - fmt.println("rawptr"); + syntax(); +} + +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 as 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 not just a `ptr+count` + slice: []int; compile_assert(size_of_val(slice) == 2*size_of(int)); + + // Helper type - Help the reader understand that 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--; + + + // You can now build files as a `.dll` + // `odin build_dll demo.odin` + + + // 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; // Is valid } - THINGI :: 14451; + + + // 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(); +} + +#import "atomic.odin" when ODIN_OS == "windows"; +#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 + + 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 + // E.g. this constant "number" should be able to be cast to a `f32` automatically + give cast(f32)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() { + + // while loops + while true { + break; + } + while x := 123; x < 124 { + x += 2; + } + + +/* + This only C-style for loop has now been removed + + for i := 0; i < 123; i += 1 { + } +*/ + for i : 0..<123 { + } + + for i : 0...122 { + } + + for val, idx : 12..<16 { + fmt.println(val, idx); + } + + primes := [...]int{2, 3, 5, 7, 11, 13, 17, 19}; + + for p : primes { + fmt.println(p); + } + + // Pointers to arrays, slices, or strings are allowed + for _ : ^primes { + // ignore the value and just iterate across it + } + + + + name := "你好,世界"; + fmt.println(name); + for r : name { + compile_assert(type_of_val(r) == rune); + fmt.printf("%r\n", r); + } + + when false { + while i := 0; i < name.count { + r, size := utf8.decode_rune(name[i:]); + i += size; + fmt.printf("%c\n", r); + } + } + + + + + + // Emulate a C-style loop (not exactly the same though) + while x := 0; x < 10 { + defer x += 2; + + /* rest of the code */ + // If `break` is used, the `defer` is still called so it's not the same + // as a C-style for loop + } + + + + procedure_overloading(); +} + + +procedure_overloading :: proc() { THINGF :: 14451.1; - - a: i32 = #line; - b: f32; - c: rawptr; - fmt.println(foo(^a)); - foo(^b); - foo(c); - // foo(nil); - atomic.store(^a, 1); + THINGI :: 14451; foo :: proc() { fmt.printf("Zero args\n"); @@ -42,11 +249,33 @@ main :: proc() { } foo(); - // foo(THINGI); foo(THINGF); + // foo(THINGI); foo(cast(int)THINGI); - fmt.println(THINGI); fmt.println(THINGF); + fmt.println(THINGI); + + + + + foo :: proc(x: ^i32) -> (int, int) { + fmt.println("^int"); + return 123, cast(int)(x^); + } + foo :: proc(x: rawptr) { + fmt.println("rawptr"); + } + + + a: i32 = #line; + b: f32; + c: rawptr; + fmt.println(foo(^a)); + foo(^b); + foo(c); + // foo(nil); + atomic.store(^a, 1); + f: proc(); f = foo; diff --git a/core/fmt.odin b/core/fmt.odin index ae8004d84..322350d7f 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -233,7 +233,7 @@ bprint :: proc(buf: ^Buffer, args: ...any) -> int { for arg, i : args { is_string := arg.data != nil && is_type_string(arg.type_info); if i > 0 && !is_string && !prev_string { - buffer_write_rune(buf, ' '); + buffer_write_byte(buf, ' '); } fmt_value(^fi, args[i], 'v'); prev_string = is_string; @@ -247,11 +247,11 @@ bprintln :: proc(buf: ^Buffer, args: ...any) -> int { for arg, i : args { if i > 0 { - buffer_write_rune(buf, ' '); + buffer_write_byte(buf, ' '); } fmt_value(^fi, args[i], 'v'); } - buffer_write_rune(buf, '\n'); + buffer_write_byte(buf, '\n'); return buf.length; } @@ -532,7 +532,8 @@ fmt_int :: proc(fi: ^Fmt_Info, u: u64, signed: bool, verb: rune) { case 'd': fmt_integer(fi, u, 10, signed, __DIGITS_LOWER); case 'x': fmt_integer(fi, u, 16, signed, __DIGITS_LOWER); case 'X': fmt_integer(fi, u, 16, signed, __DIGITS_UPPER); - case 'c': fmt_rune(fi, cast(rune)u); + case 'c', 'r': + fmt_rune(fi, cast(rune)u); case 'U': r := cast(rune)u; if r < 0 || r > utf8.MAX_RUNE { diff --git a/core/utf8.odin b/core/utf8.odin index a8dd3c19b..7730f94be 100644 --- a/core/utf8.odin +++ b/core/utf8.odin @@ -8,9 +8,29 @@ UTF_MAX :: 4; SURROGATE_MIN :: 0xd800; SURROGATE_MAX :: 0xdfff; +T1 :: 0b0000_0000; +TX :: 0b1000_0000; +T2 :: 0b1100_0000; +T3 :: 0b1110_0000; +T4 :: 0b1111_0000; +T5 :: 0b1111_1000; + +MASKX :: 0b0011_1111; +MASK2 :: 0b0001_1111; +MASK3 :: 0b0000_1111; +MASK4 :: 0b0000_0111; + +RUNE1_MAX :: 1<<7 - 1; +RUNE2_MAX :: 1<<11 - 1; +RUNE3_MAX :: 1<<16 - 1; + +// The default lowest and highest continuation byte. +LOCB :: 0b1000_0000; +HICB :: 0b1011_1111; + Accept_Range :: struct { lo, hi: u8 } -accept_ranges := [5]Accept_Range{ +immutable accept_ranges := [5]Accept_Range{ {0x80, 0xbf}, {0xa0, 0xbf}, {0x80, 0x9f}, @@ -18,7 +38,7 @@ accept_ranges := [5]Accept_Range{ {0x80, 0x8f}, }; -accept_sizes := [256]byte{ +immutable accept_sizes := [256]byte{ 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0f 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1f 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2f @@ -68,7 +88,7 @@ encode_rune :: proc(r: rune) -> ([4]byte, int) { buf[0] = 0xf0 | cast(byte)(r>>18); buf[1] = 0x80 | cast(byte)(r>>12) & mask; buf[2] = 0x80 | cast(byte)(r>>6) & mask; - buf[3] = 0x80 | cast(byte)r & mask; + buf[3] = 0x80 | cast(byte)r & mask; return buf, 4; } @@ -77,43 +97,36 @@ decode_rune :: proc(s: string) -> (rune, int) { if n < 1 { return RUNE_ERROR, 0; } - b0 := s[0]; - x := accept_sizes[b0]; - if x >= 0xf0 { - mask := (cast(rune)x << 31) >> 31; // all zeros or all ones - return cast(rune)b0 &~ mask | RUNE_ERROR&mask, 1; + s0 := s[0]; + x := accept_sizes[s0]; + if x >= 0xF0 { + mask := cast(rune)(x) << 31 >> 31; // NOTE(bill): Create 0x0000 or 0xffff. + return cast(rune)(s[0])&~mask | RUNE_ERROR&mask, 1; } - size := x & 7; - ar := accept_ranges[x>>4]; - if n < cast(int)size { + sz := x & 7; + accept := accept_ranges[x>>4]; + if n < cast(int)sz { return RUNE_ERROR, 1; } b1 := s[1]; - if b1 < ar.lo || ar.hi < b1 { + if b1 < accept.lo || accept.hi < b1 { return RUNE_ERROR, 1; } - - MASK_X :: 0b00111111; - MASK_2 :: 0b00011111; - MASK_3 :: 0b00001111; - MASK_4 :: 0b00000111; - - if size == 2 { - return cast(rune)(b0&MASK_2)<<6 | cast(rune)(b1&MASK_X), 2; + if sz == 2 { + return cast(rune)(s0&MASK2)<<6 | cast(rune)(b1&MASKX), 2; } b2 := s[2]; - if b2 < 0x80 || 0xbf < b2 { + if b2 < LOCB || HICB < b2 { return RUNE_ERROR, 1; } - if size == 3 { - return cast(rune)(b0&MASK_3)<<12 | cast(rune)(b1&MASK_X)<<6 | cast(rune)(b2&MASK_X), 3; + if sz == 3 { + return cast(rune)(s0&MASK3)<<12 | cast(rune)(b1&MASKX)<<6 | cast(rune)(b2&MASKX), 3; } b3 := s[3]; - if b3 < 0x80 || 0xbf < b3 { + if b3 < LOCB || HICB < b3 { return RUNE_ERROR, 1; } - return cast(rune)(b0&MASK_4)<<18 | cast(rune)(b1&MASK_X)<<12 | cast(rune)(b3&MASK_X)<<6 | cast(rune)(b3&MASK_X), 4; - + return cast(rune)(s0&MASK4)<<18 | cast(rune)(b1&MASKX)<<12 | cast(rune)(b2&MASKX)<<6 | cast(rune)(b3&MASKX), 4; } diff --git a/src/check_decl.c b/src/check_decl.c index f2a62c957..39c924ebd 100644 --- a/src/check_decl.c +++ b/src/check_decl.c @@ -477,9 +477,19 @@ void check_entity_decl(Checker *c, Entity *e, DeclInfo *d, Type *named_type) { void check_proc_body(Checker *c, Token token, DeclInfo *decl, Type *type, AstNode *body) { GB_ASSERT(body->kind == AstNode_BlockStmt); + String proc_name = {0}; + if (token.kind == Token_Ident) { + proc_name = token.string; + } else { + // TODO(bill): Better name + proc_name = str_lit("(anonymous-procedure)"); + } + CheckerContext old_context = c->context; c->context.scope = decl->scope; c->context.decl = decl; + c->context.proc_name = proc_name; + GB_ASSERT(type->kind == Type_Proc); if (type->Proc.param_count > 0) { diff --git a/src/check_expr.c b/src/check_expr.c index 039795fae..273f237fb 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -3892,6 +3892,16 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint } else if (str_eq(bd->name, str_lit("line"))) { o->type = t_untyped_integer; o->value = make_exact_value_integer(bd->token.pos.line); + } else if (str_eq(bd->name, str_lit("procedure"))) { + if (c->proc_stack.count == 0) { + error_node(node, "#procedure may only be used within procedures"); + o->type = t_untyped_string; + o->value = make_exact_value_string(str_lit("")); + } else { + o->type = t_untyped_string; + o->value = make_exact_value_string(c->context.proc_name); + } + } else { GB_PANIC("Unknown basic basic directive"); } @@ -3962,9 +3972,11 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint goto error; } else if (operands.count == 1) { Operand operand = operands.e[0]; - if (type_hint != NULL) { - convert_to_typed(c, &operand, type_hint, 0); + Type *th = type_hint != NULL ? type_hint : c->context.type_hint; + if (th != NULL) { + convert_to_typed(c, &operand, th, 0); } + // IMPORTANT NOTE(bill): This type could be untyped!!! o->type = default_type(operand.type); o->mode = Addressing_Value; } else { @@ -4012,10 +4024,14 @@ ExprKind check__expr_base(Checker *c, Operand *o, AstNode *node, Type *type_hint goto error; } + CheckerContext prev_context = c->context; + c->context.type_hint = type_hint; check_open_scope(c, node); check_stmt_list(c, be->stmts, Stmt_GiveAllowed); check_close_scope(c); + c->context = prev_context; + AstNode *give_node = NULL; if (!check_is_giving(node, &give_node) || give_node == NULL) { error_node(node, "Missing give statement at end of block expression"); diff --git a/src/checker.c b/src/checker.c index 645a37f89..aecaf66d9 100644 --- a/src/checker.c +++ b/src/checker.c @@ -251,6 +251,8 @@ typedef struct CheckerContext { DeclInfo * decl; u32 stmt_state_flags; bool in_defer; // TODO(bill): Actually handle correctly + String proc_name; + Type * type_hint; } CheckerContext; // NOTE(bill): Symbol tables @@ -276,8 +278,8 @@ typedef struct Checker { BaseTypeSizes sizes; Scope * global_scope; Array(ProcedureInfo) procs; // NOTE(bill): Procedures to check - Array(DelayedDecl) delayed_imports; - Array(DelayedDecl) delayed_foreign_libraries; + Array(DelayedDecl) delayed_imports; + Array(DelayedDecl) delayed_foreign_libraries; gbArena arena; diff --git a/src/exact_value.c b/src/exact_value.c index 141851546..2a8b4eb20 100644 --- a/src/exact_value.c +++ b/src/exact_value.c @@ -54,30 +54,57 @@ ExactValue make_exact_value_string(String string) { return result; } -ExactValue make_exact_value_integer_from_string(String string) { - // TODO(bill): Allow for numbers with underscores in them - ExactValue result = {ExactValue_Integer}; - i32 base = 10; - if (string.len > 2 && string.text[0] == '0') { - switch (string.text[1]) { - case 'b': base = 2; break; - case 'o': base = 8; break; - case 'd': base = 10; break; - case 'x': base = 16; break; - } - } - - result.value_integer = gb_str_to_i64(cast(char *)string.text, NULL, base); - - return result; -} - ExactValue make_exact_value_integer(i64 i) { ExactValue result = {ExactValue_Integer}; result.value_integer = i; return result; } +ExactValue make_exact_value_integer_from_string(String string) { + // TODO(bill): Allow for numbers with underscores in them + i32 base = 10; + bool has_prefix = false; + if (string.len > 2 && string.text[0] == '0') { + switch (string.text[1]) { + case 'b': base = 2; has_prefix = true; break; + case 'o': base = 8; has_prefix = true; break; + case 'd': base = 10; has_prefix = true; break; + case 'x': base = 16; has_prefix = true; break; + } + } + + u8 *text = string.text; + isize len = string.len; + if (has_prefix) { + text += 2; + len -= 2; + } + + i64 result = 0; + for (isize i = 0; i < len; i++) { + Rune r = cast(Rune)text[i]; + if (r == '_') { + continue; + } + i64 v = 0; + if (gb_char_is_digit(r)) { + v = r - '0'; + } else if (gb_char_is_hex_digit(r)) { + v = gb_hex_digit_to_int(r); + } else { + break; + } + + result *= base; + result += v; + } + + + return make_exact_value_integer(result); +} + + + ExactValue make_exact_value_float_from_string(String string) { // TODO(bill): Allow for numbers with underscores in them ExactValue result = {ExactValue_Float}; diff --git a/src/parser.c b/src/parser.c index c208a260f..f8a732fb4 100644 --- a/src/parser.c +++ b/src/parser.c @@ -1770,11 +1770,7 @@ AstNode *parse_operand(AstFile *f, bool lhs) { case Token_Hash: { Token token = expect_token(f, Token_Hash); Token name = expect_token(f, Token_Ident); - if (str_eq(name.string, str_lit("file"))) { - return make_basic_directive(f, token, name.string); - } else if (str_eq(name.string, str_lit("line"))) { - return make_basic_directive(f, token, name.string); - } else if (str_eq(name.string, str_lit("run"))) { + if (str_eq(name.string, str_lit("run"))) { AstNode *expr = parse_expr(f, false); operand = make_run_expr(f, token, name, expr); if (unparen_expr(expr)->kind != AstNode_CallExpr) { @@ -1782,6 +1778,9 @@ AstNode *parse_operand(AstFile *f, bool lhs) { operand = make_bad_expr(f, token, f->curr_token); } warning(token, "#run is not yet implemented"); + } else if (str_eq(name.string, str_lit("file"))) { return make_basic_directive(f, token, name.string); + } else if (str_eq(name.string, str_lit("line"))) { return make_basic_directive(f, token, name.string); + } else if (str_eq(name.string, str_lit("procedure"))) { return make_basic_directive(f, token, name.string); } else { operand = make_tag_expr(f, token, name, parse_expr(f, false)); } diff --git a/src/tokenizer.c b/src/tokenizer.c index eadfc767d..79cdda258 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -462,12 +462,15 @@ gb_inline i32 digit_value(Rune r) { return 16; // NOTE(bill): Larger than highest possible } -gb_inline void scan_mantissa(Tokenizer *t, i32 base) { - // TODO(bill): Allow for underscores in numbers as a number separator - // TODO(bill): Is this a good idea? - // while (digit_value(t->curr_rune) < base || t->curr_rune == '_') - while (digit_value(t->curr_rune) < base) { - advance_to_next_rune(t); +gb_inline void scan_mantissa(Tokenizer *t, i32 base, bool allow_underscore) { + if (allow_underscore) { + while (digit_value(t->curr_rune) < base || t->curr_rune == '_') { + advance_to_next_rune(t); + } + } else { + while (digit_value(t->curr_rune) < base) { + advance_to_next_rune(t); + } } } @@ -482,7 +485,7 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) { if (seen_decimal_point) { token.kind = Token_Float; - scan_mantissa(t, 10); + scan_mantissa(t, 10, true); goto exponent; } @@ -491,31 +494,31 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) { advance_to_next_rune(t); if (t->curr_rune == 'b') { // Binary advance_to_next_rune(t); - scan_mantissa(t, 2); + scan_mantissa(t, 2, true); if (t->curr - prev <= 2) { token.kind = Token_Invalid; } } else if (t->curr_rune == 'o') { // Octal advance_to_next_rune(t); - scan_mantissa(t, 8); + scan_mantissa(t, 8, true); if (t->curr - prev <= 2) { token.kind = Token_Invalid; } } else if (t->curr_rune == 'd') { // Decimal advance_to_next_rune(t); - scan_mantissa(t, 10); + scan_mantissa(t, 10, true); if (t->curr - prev <= 2) { token.kind = Token_Invalid; } } else if (t->curr_rune == 'x') { // Hexadecimal advance_to_next_rune(t); - scan_mantissa(t, 16); + scan_mantissa(t, 16, true); if (t->curr - prev <= 2) { token.kind = Token_Invalid; } } else { seen_decimal_point = false; - scan_mantissa(t, 10); + scan_mantissa(t, 10, true); if (t->curr_rune == '.' || t->curr_rune == 'e' || t->curr_rune == 'E') { seen_decimal_point = true; @@ -527,7 +530,7 @@ Token scan_number_to_token(Tokenizer *t, bool seen_decimal_point) { return token; } - scan_mantissa(t, 10); + scan_mantissa(t, 10, true); fraction: if (t->curr_rune == '.') { @@ -540,7 +543,7 @@ fraction: goto end; } token.kind = Token_Float; - scan_mantissa(t, 10); + scan_mantissa(t, 10, true); } exponent: @@ -550,7 +553,7 @@ exponent: if (t->curr_rune == '-' || t->curr_rune == '+') { advance_to_next_rune(t); } - scan_mantissa(t, 10); + scan_mantissa(t, 10, false); } end: