From c59f6b7d0b582131bed4450bbb9aa1a71d5a01af Mon Sep 17 00:00:00 2001 From: Ginger Bill Date: Sun, 26 Feb 2017 00:44:26 +0000 Subject: [PATCH] ++ -- statements; add strconv.odin (and replace some of the fmt procs); Fix ~ on 64 bit constants; Fix integer casts from smaller to larger size --- code/demo.odin | 15 +- core/_preload.odin | 6 +- core/atomic.odin | 4 +- core/decimal.odin | 258 ++++++++++++++++++++++++++++++++++ core/fmt.odin | 214 ++++++++++++---------------- core/hash.odin | 6 +- core/mem.odin | 6 +- core/strconv.odin | 337 +++++++++++++++++++++++++++++++++++++++++++++ core/strings.odin | 2 +- core/sync.odin | 6 +- core/utf8.odin | 58 +++++++- src/check_expr.c | 27 ++-- src/check_stmt.c | 48 ++++++- src/exact_value.c | 7 +- src/ir.c | 23 +++- src/parser.c | 22 +++ src/tokenizer.c | 18 +-- 17 files changed, 881 insertions(+), 176 deletions(-) create mode 100644 core/decimal.odin create mode 100644 core/strconv.odin diff --git a/code/demo.odin b/code/demo.odin index c548a9430..345ac7922 100644 --- a/code/demo.odin +++ b/code/demo.odin @@ -5,14 +5,19 @@ #import "mem.odin"; #import "opengl.odin"; #import "os.odin"; - -foo :: proc(x: int) -> f32 { - return 123; -} +#import "strconv.odin"; main :: proc() { + buf: [64]byte; + // len := strconv.generic_ftoa(buf[:], 123.5431, 'f', 4, 64); + x := 624.123; + s := strconv.format_float(buf[:], x, 'f', 6, 64); + fmt.println(s); + fmt.printf("%.3f\n", x); + + i := 123; + fmt.println(123); - fmt.printf("%T\n", foo); when false { /* diff --git a/core/_preload.odin b/core/_preload.odin index de01bb244..56f9bfa45 100644 --- a/core/_preload.odin +++ b/core/_preload.odin @@ -447,7 +447,7 @@ __dynamic_array_append_nothing :: proc(array_: rawptr, elem_size, elem_align: in data := cast(^byte)array.data; assert(data != nil); mem.zero(data + (elem_size*array.count), elem_size); - array.count += 1; + array.count++; return array.count; } @@ -506,7 +506,7 @@ __dynamic_map_rehash :: proc(using header: __Map_Header, new_count: int) { nm.hashes[i] = -1; } - for i := 0; i < nm.entries.count; i += 1 { + for i := 0; i < nm.entries.count; i++ { entry_header := __dynamic_map_get_entry(new_header, i); data := cast(^byte)entry_header; @@ -645,7 +645,7 @@ __dynamic_map_erase :: proc(using h: __Map_Header, fr: __Map_Find_Result) { } if fr.entry_index == m.entries.count-1 { - m.entries.count -= 1; + m.entries.count--; } mem.copy(__dynamic_map_get_entry(h, fr.entry_index), __dynamic_map_get_entry(h, m.entries.count-1), entry_size); last := __dynamic_map_find(h, __dynamic_map_get_entry(h, fr.entry_index).key); diff --git a/core/atomic.odin b/core/atomic.odin index 3db437a4b..ec7900f0d 100644 --- a/core/atomic.odin +++ b/core/atomic.odin @@ -37,7 +37,7 @@ spin_lock :: proc(a: ^i32, time_out: int) -> bool { // NOTE(bill) time_out = -1 old_value := compare_exchange(a, 1, 0); counter := 0; for old_value != 0 && (time_out < 0 || counter < time_out) { - counter += 1; + counter++; yield_thread(); old_value = compare_exchange(a, 1, 0); mfence(); @@ -81,7 +81,7 @@ spin_lock :: proc(a: ^i64, time_out: int) -> bool { // NOTE(bill) time_out = -1 old_value := compare_exchange(a, 1, 0); counter := 0; for old_value != 0 && (time_out < 0 || counter < time_out) { - counter += 1; + counter++; yield_thread(); old_value = compare_exchange(a, 1, 0); mfence(); diff --git a/core/decimal.odin b/core/decimal.odin new file mode 100644 index 000000000..90582d20c --- /dev/null +++ b/core/decimal.odin @@ -0,0 +1,258 @@ +// #import "fmt.odin"; +// Multiple precision decimal numbers +// NOTE: This is only for floating point printing and nothing else + +Decimal :: struct { + d: [384]byte, // big-endian digits + ndu: int, + dp: int, + neg: bool, + trunc: bool, +} + +decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string { + digit_zero :: proc(buf: []byte) -> int { + for _, i in buf { + buf[i] = '0'; + } + return buf.count; + } + + + n := 10 + a.ndu + abs(a.dp); + + // TODO(bill): make this work with a buffer that's not big enough + assert(buf.count >= n); + buf = buf[:n]; + + if a.ndu == 0 { + buf[0] = '0'; + return cast(string)buf[0:1]; + } + + w := 0; + if a.dp <= 0 { + buf[w] = '0'; w++; + buf[w] = '.'; w++; + w += digit_zero(buf[w: w-a.dp]); + w += copy(buf[w:], a.d[0:a.ndu]); + } else if a.dp < a.ndu { + w += copy(buf[w:], a.d[0:a.dp]); + buf[w] = '.'; w++; + w += copy(buf[w:], a.d[a.dp:a.ndu]); + } else { + w += copy(buf[w:], a.d[0:a.ndu]); + w += digit_zero(buf[w : w+a.dp-a.ndu]); + } + + return cast(string)buf[0:w]; +} + +// trim trailing zeros +trim :: proc(a: ^Decimal) { + for a.ndu > 0 && a.d[a.ndu-1] == '0' { + a.ndu--; + } + if a.ndu == 0 { + a.dp = 0; + } +} + + +assign :: proc(a: ^Decimal, i: u64) { + buf: [32]byte; + n := 0; + for i > 0 { + j := i/10; + i -= 10*j; + buf[n] = cast(byte)('0'+i); + n++; + i = j; + } + + a.ndu = 0; + for n--; n >= 0; n-- { + a.d[a.ndu] = buf[n]; + a.ndu++; + } + a.dp = a.ndu; + trim(a); +} + +uint_size :: 8*size_of(uint); +max_shift :: uint_size-4; + +shift_right :: proc(a: ^Decimal, k: uint) { + r := 0; // read index + w := 0; // write index + + n: uint; + for ; n>>k == 0; r++ { + if r >= a.ndu { + if n == 0 { + // Just in case + a.ndu = 0; + return; + } + for n>>k == 0 { + n = n * 10; + r++; + } + break; + } + c := cast(uint)a.d[r]; + n = n*10 + c - '0'; + } + a.dp -= r-1; + + mask: uint = (1<>k; + n &= mask; + a.d[w] = cast(byte)('0' + dig); + w++; + n = n*10 + c - '0'; + } + + for n > 0 { + dig := n>>k; + n &= mask; + if w < a.d.count { + a.d[w] = cast(byte)('0' + dig); + w++; + } else if dig > 0 { + a.trunc = true; + } + n *= 10; + } + + + a.ndu = w; + trim(a); +} + +shift_left :: proc(a: ^Decimal, k: uint) { + delta := cast(int)(k/4); + + r := a.ndu; // read index + w := a.ndu+delta; // write index + + n: uint; + for r--; r >= 0; r-- { + n += (cast(uint)a.d[r] - '0') << k; + quo := n/10; + rem := n - 10*quo; + w--; + if w < a.d.count { + a.d[w] = cast(byte)('0' + rem); + } else if rem != 0 { + a.trunc = true; + } + n = quo; + } + + for n > 0 { + quo := n/10; + rem := n - 10*quo; + w--; + if w < a.d.count { + a.d[w] = cast(byte)('0' + rem); + } else if rem != 0 { + a.trunc = true; + } + n = quo; + } + + a.ndu += delta; + a.ndu = min(a.ndu, a.d.count); + a.dp += delta; + trim(a); +} + +shift :: proc(a: ^Decimal, k: int) { + match { + case a.ndu == 0: + // no need to update + case k > 0: + for k > max_shift { + shift_left(a, max_shift); + k -= max_shift; + } + shift_left(a, cast(uint)k); + + + case k < 0: + for k < -max_shift { + shift_right(a, max_shift); + k += max_shift; + } + shift_right(a, cast(uint)-k); + } +} + +can_round_up :: proc(a: ^Decimal, nd: int) -> bool { + if nd < 0 || nd >= a.ndu { return false ; } + if a.d[nd] == '5' && nd+1 == a.ndu { + if a.trunc { + return true; + } + return nd > 0 && (a.d[nd-1]-'0')%2 != 0; + } + + return a.d[nd] >= '5'; +} + +round :: proc(a: ^Decimal, nd: int) { + if nd < 0 || nd >= a.ndu { return; } + if can_round_up(a, nd) { + round_up(a, nd); + } else { + round_down(a, nd); + } +} + +round_up :: proc(a: ^Decimal, nd: int) { + if nd < 0 || nd >= a.ndu { return; } + + for i := nd-1; i >= 0; i-- { + if c := a.d[i]; c < '9' { + a.d[i]++; + a.ndu = i+1; + return; + } + } + + // Number is just 9s + a.d[0] = '1'; + a.ndu = 1; + a.dp++; +} + +round_down :: proc(a: ^Decimal, nd: int) { + if nd < 0 || nd >= a.ndu { return; } + a.ndu = nd; + trim(a); +} + + +// Extract integer part, rounded appropriately. There are no guarantees about overflow. +rounded_integer :: proc(a: ^Decimal) -> u64 { + if a.dp > 20 { + return 0xffff_ffff_ffff_ffff; + } + i: int; + n: u64 = 0; + m := min(a.dp, a.ndu); + for i = 0; i < m; i++ { + n = n*10 + cast(u64)(a.d[i]-'0'); + } + for ; i < a.dp; i++ { + n *= 10; + } + if can_round_up(a, a.dp) { + n++; + } + return n; +} diff --git a/core/fmt.odin b/core/fmt.odin index 80517ee95..bdb04d7e7 100644 --- a/core/fmt.odin +++ b/core/fmt.odin @@ -2,6 +2,7 @@ #import "mem.odin"; #import "utf8.odin"; #import "types.odin"; +#import "strconv.odin"; _BUFFER_SIZE :: 1<<12; @@ -26,7 +27,7 @@ buffer_write_string :: proc(buf: ^Buffer, s: string) { buffer_write_byte :: proc(buf: ^Buffer, b: byte) { if buf.length < buf.data.count { buf.data[buf.length] = b; - buf.length += 1; + buf.length++; } } buffer_write_rune :: proc(buf: ^Buffer, r: rune) { @@ -226,7 +227,7 @@ buffer_write_type :: proc(buf: ^Buffer, ti: ^Type_Info) { buffer_write_string(buf, name); buffer_write_string(buf, ": "); buffer_write_type(buf, cf.types[i]); - total_count += 1; + total_count++; } for name, i in info.variant_names { if total_count > 0 || i > 0 { @@ -345,7 +346,7 @@ parse_int :: proc(s: string, offset: int) -> (result: int, offset: int, ok: bool if !is_digit(c) { break; } - i += 1; + i++; result *= 10; result += cast(int)(c - '0'); @@ -459,17 +460,16 @@ fmt_write_padding :: proc(fi: ^Fmt_Info, width: int) { fi.buf.length += count; } -fmt_integer :: proc(fi: ^Fmt_Info, u: u64, base: int, signed: bool, digits: string) { - negative := signed && cast(i64)u < 0; - if signed { - u = cast(u64)abs(cast(i64)u); +_write_int :: proc(fi: ^Fmt_Info, u: u64, base: int, neg: bool, digits: string) { + if neg { + u = -u; } - buf: [256]byte; + BUF_SIZE :: 256; if fi.width_set || fi.prec_set { width := fi.width + fi.prec + 3; - if width > buf.count { + if width > BUF_SIZE { // TODO(bill):???? - panic("fmt_integer buffer overrun. Width and precision too big"); + panic("_write_int buffer overrun. Width and precision too big"); } } @@ -485,95 +485,64 @@ fmt_integer :: proc(fi: ^Fmt_Info, u: u64, base: int, signed: bool, digits: stri } } else if fi.zero && fi.width_set { prec = fi.width; - if negative || fi.plus || fi.space { + if neg || fi.plus || fi.space { // There needs to be space for the "sign" - prec -= 1; + prec--; } } - i := buf.count; - match base { case 2, 8, 10, 16: break; default: - panic("fmt_integer: unknown base, whoops"); + panic("_write_int: unknown base, whoops"); } - for b := cast(u64)base; u >= b; { - i -= 1; - next := u / b; - buf[i] = digits[u%b]; - u = next; - } - i -= 1; - buf[i] = digits[u]; - for i > 0 && prec > buf.count-i { - i -= 1; - buf[i] = '0'; - } - - if fi.hash { - i -= 1; - match base { - case 2: buf[i] = 'b'; - case 8: buf[i] = 'o'; - case 10: buf[i] = 'd'; - case 16: buf[i] = digits[16]; - } - i -= 1; - buf[i] = '0'; - } - - if negative { - i -= 1; - buf[i] = '-'; - } else if fi.plus { - i -= 1; - buf[i] = '+'; - } else if fi.space { - i -= 1; - buf[i] = ' '; - } + buf: [256]byte; + flags: strconv.Int_Flag; + if fi.hash { flags |= strconv.Int_Flag.PREFIX; } + if fi.plus { flags |= strconv.Int_Flag.PLUS; } + if fi.space { flags |= strconv.Int_Flag.SPACE; } + s := strconv.format_bits(buf[:], u, base, neg, digits, flags); old_zero := fi.zero; defer fi.zero = old_zero; fi.zero = false; if !fi.width_set || fi.width == 0 { - buffer_write(fi.buf, buf[i:]); + buffer_write_string(fi.buf, s); } else { - width := fi.width - utf8.rune_count(cast(string)buf[i:]); + width := fi.width - utf8.rune_count(s); if width > 0 { if fi.minus { // Right pad - buffer_write(fi.buf, buf[i:]); + buffer_write_string(fi.buf, s); fmt_write_padding(fi, width); } else { // Left pad fmt_write_padding(fi, width); - buffer_write(fi.buf, buf[i:]); + buffer_write_string(fi.buf, s); } } } } -__DIGITS_LOWER := "0123456789abcdefx"; -__DIGITS_UPPER := "0123456789ABCDEFX"; +immutable __DIGITS_LOWER := "0123456789abcdefx"; +immutable __DIGITS_UPPER := "0123456789ABCDEFX"; fmt_rune :: proc(fi: ^Fmt_Info, r: rune) { buffer_write_rune(fi.buf, r); } -fmt_int :: proc(fi: ^Fmt_Info, u: u64, signed: bool, verb: rune) { +fmt_int :: proc(fi: ^Fmt_Info, u: u64, neg: bool, verb: rune) { match verb { - case 'v': fmt_integer(fi, u, 10, signed, __DIGITS_LOWER); - case 'b': fmt_integer(fi, u, 2, signed, __DIGITS_LOWER); - case 'o': fmt_integer(fi, u, 8, signed, __DIGITS_LOWER); - 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 'v': _write_int(fi, u, 10, neg, __DIGITS_LOWER); + case 'b': _write_int(fi, u, 2, neg, __DIGITS_LOWER); + case 'o': _write_int(fi, u, 8, neg, __DIGITS_LOWER); + case 'd': _write_int(fi, u, 10, neg, __DIGITS_LOWER); + case 'x': _write_int(fi, u, 16, neg, __DIGITS_LOWER); + case 'X': _write_int(fi, u, 16, neg, __DIGITS_UPPER); case 'c', 'r': fmt_rune(fi, cast(rune)u); case 'U': @@ -582,7 +551,7 @@ fmt_int :: proc(fi: ^Fmt_Info, u: u64, signed: bool, verb: rune) { fmt_bad_verb(fi, verb); } else { buffer_write_string(fi.buf, "U+"); - fmt_integer(fi, u, 16, false, __DIGITS_UPPER); + _write_int(fi, u, 16, false, __DIGITS_UPPER); } default: @@ -592,67 +561,60 @@ fmt_int :: proc(fi: ^Fmt_Info, u: u64, signed: bool, verb: rune) { fmt_float :: proc(fi: ^Fmt_Info, v: f64, bit_size: int, verb: rune) { + pad :: proc(fi: ^Fmt_Info, s: string) { + if !fi.width_set || fi.width == 0 { + buffer_write_string(fi.buf, s); + return; + } + width := fi.width - utf8.rune_count(s); + if fi.minus { // right pad + buffer_write_string(fi.buf, s); + fmt_write_padding(fi, width); + } else { // left pad + fmt_write_padding(fi, width); + buffer_write_string(fi.buf, s); + } + } + match verb { // case 'e', 'E', 'f', 'F', 'g', 'G', 'v': // case 'f', 'F', 'v': - // TODO(bill): This is a shit copy from gb.h and I really need a decent implementation case 'f', 'F', 'v': - width := 0; - if fi.width_set { - width = max(fi.width, 0); - } - prec := 3; + prec: int = 3; if fi.prec_set { - prec = max(fi.prec, 0); + prec = fi.prec; + } + buf: [128]byte; + str := strconv.format_float(buf[1:], v, 'f', prec, bit_size); + str = cast(string)buf[:str.count+1]; + if str[1] == '+' || str[1] == '-' { + str = str[1:]; + } else { + str[0] = '+'; } - if v == 0 { - buffer_write_byte(fi.buf, '0'); - if fi.hash && width > 0 { - buffer_write_byte(fi.buf, '.'); + if fi.space && !fi.plus && str[0] == '+' { + str[0] = ' '; + } + + if str[1] == 'N' && str[1] == 'I' { + buffer_write_string(fi.buf, str); + return; + } + + if fi.plus || str[0] != '+' { + if fi.zero && fi.width_set && fi.width > str.count { + buffer_write_byte(fi.buf, str[0]); + fmt_write_padding(fi, fi.width - str.count); + buffer_write_string(fi.buf, str[1:]); + } else { + pad(fi, str); } } else { - signed := v < 0; - v = abs(v); - - if signed { - buffer_write_byte(fi.buf, '-'); - } - - val := cast(u64)v; - fi.minus = false; - fi.width = 0; - fi.prec = 0; - // TODO(bill): Write integer to buffer than use this crap - fmt_integer(fi, val, 10, false, __DIGITS_LOWER); - - if fi.hash || prec > 0 { - arg := v - cast(f64)val; - mult: f64 = 10; - buffer_write_byte(fi.buf, '.'); - for _ in 0.. 0 { - fill: byte = ' '; - match { - case fi.zero: fill = '0'; - case fi.space: fill = ' '; - } - - for width > 0 { - width -= 1; - buffer_write_byte(fi.buf, fill); - } - } default: fmt_bad_verb(fi, verb); return; @@ -679,7 +641,7 @@ fmt_pointer :: proc(fi: ^Fmt_Info, p: rawptr, verb: rune) { if !fi.hash || verb == 'v' { buffer_write_string(fi.buf, "0x"); } - fmt_integer(fi, u, 16, false, __DIGITS_UPPER); + _write_int(fi, u, 16, false, __DIGITS_UPPER); } fmt_enum :: proc(fi: ^Fmt_Info, v: any, verb: rune) { @@ -957,11 +919,11 @@ fmt_arg :: proc(fi: ^Fmt_Info, arg: any, verb: rune) { case f32: fmt_float(fi, cast(f64)a, 32, verb); case f64: fmt_float(fi, a, 64, verb); - case int: fmt_int(fi, cast(u64)a, true, verb); - case i8: fmt_int(fi, cast(u64)a, true, verb); - case i16: fmt_int(fi, cast(u64)a, true, verb); - case i32: fmt_int(fi, cast(u64)a, true, verb); - case i64: fmt_int(fi, cast(u64)a, true, verb); + case int: fmt_int(fi, cast(u64)a, a < 0, verb); + case i8: fmt_int(fi, cast(u64)a, a < 0, verb); + case i16: fmt_int(fi, cast(u64)a, a < 0, verb); + case i32: fmt_int(fi, cast(u64)a, a < 0, verb); + case i64: fmt_int(fi, cast(u64)a, a < 0, verb); case uint: fmt_int(fi, cast(u64)a, false, verb); case u8: fmt_int(fi, cast(u64)a, false, verb); case u16: fmt_int(fi, cast(u64)a, false, verb); @@ -984,7 +946,7 @@ bprintf :: proc(b: ^Buffer, fmt: string, args: ...any) -> int { prev_i := i; for i < end && fmt[i] != '%' { - i += 1; + i++; } if i > prev_i { buffer_write_string(b, fmt[prev_i:i]); @@ -994,10 +956,10 @@ bprintf :: proc(b: ^Buffer, fmt: string, args: ...any) -> int { } // Process a "verb" - i += 1; + i++; - for ; i < end; i += 1 { + for ; i < end; i++ { skip_loop := false; c := fmt[i]; match c { @@ -1025,7 +987,7 @@ bprintf :: proc(b: ^Buffer, fmt: string, args: ...any) -> int { // Width if i < end && fmt[i] == '*' { - i += 1; + i++; fi.width, arg_index, fi.width_set = int_from_arg(args, arg_index); if !fi.width_set { buffer_write_string(b, "%!(BAD WIDTH)"); @@ -1046,13 +1008,13 @@ bprintf :: proc(b: ^Buffer, fmt: string, args: ...any) -> int { // Precision if i < end && fmt[i] == '.' { - i += 1; + i++; if was_prev_index { // %[6].2d fi.good_arg_index = false; } if i < end && fmt[i] == '*' { arg_index, i, was_prev_index = _arg_number(^fi, arg_index, fmt, i, args.count); - i += 1; + i++; fi.prec, arg_index, fi.prec_set = int_from_arg(args, arg_index); if fi.prec < 0 { fi.prec = 0; @@ -1091,7 +1053,7 @@ bprintf :: proc(b: ^Buffer, fmt: string, args: ...any) -> int { buffer_write_string(b, "%!(MISSING ARGUMENT)"); } else { fmt_arg(^fi, args[arg_index], verb); - arg_index += 1; + arg_index++; } } diff --git a/core/hash.odin b/core/hash.odin index de1ffd0b6..fb3720176 100644 --- a/core/hash.odin +++ b/core/hash.odin @@ -146,7 +146,7 @@ murmur64 :: proc(data: []byte) -> u64 { i := 0; for len >= 8 { k1, k2: u32; - k1 = data32[i]; i += 1; + k1 = data32[i]; i++; k1 *= m; k1 ~= k1>>r; k1 *= m; @@ -154,7 +154,7 @@ murmur64 :: proc(data: []byte) -> u64 { h1 ~= k1; len -= 4; - k2 = data32[i]; i += 1; + k2 = data32[i]; i++; k2 *= m; k2 ~= k2>>r; k2 *= m; @@ -165,7 +165,7 @@ murmur64 :: proc(data: []byte) -> u64 { if len >= 4 { k1: u32; - k1 = data32[i]; i += 1; + k1 = data32[i]; i++; k1 *= m; k1 ~= k1>>r; k1 *= m; diff --git a/core/mem.odin b/core/mem.odin index ce1c4a21c..5e89b38a9 100644 --- a/core/mem.odin +++ b/core/mem.odin @@ -79,7 +79,7 @@ allocation_header_fill :: proc(header: ^Allocation_Header, data: rawptr, size: i header.size = size; ptr := cast(^int)(header+1); - for i := 0; cast(rawptr)ptr < data; i += 1 { + for i := 0; cast(rawptr)ptr < data; i++ { (ptr+i)^ = -1; } } @@ -183,7 +183,7 @@ begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory { tmp: Arena_Temp_Memory; tmp.arena = a; tmp.original_count = a.memory.count; - a.temp_count += 1; + a.temp_count++; return tmp; } @@ -191,7 +191,7 @@ end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) { assert(arena.memory.count >= original_count); assert(arena.temp_count > 0); arena.memory.count = original_count; - arena.temp_count -= 1; + arena.temp_count--; } diff --git a/core/strconv.odin b/core/strconv.odin new file mode 100644 index 000000000..9f51808ad --- /dev/null +++ b/core/strconv.odin @@ -0,0 +1,337 @@ +#import . "decimal.odin"; +#import "math.odin"; +#import format "fmt.odin"; + +Int_Flag :: enum { + PREFIX = 1<<0, + PLUS = 1<<1, + SPACE = 1<<2, +} + + +parse_bool :: proc(s: string) -> (result: bool, ok: bool) { + match s { + case "1", "t", "T", "true", "TRUE", "True": + return true, true; + case "0", "f", "F", "false", "FALSE", "False": + return false, true; + } + return false, false; +} + +format_bool :: proc(buf: []byte, b: bool) -> string { + s := b ? "true" : "false"; + len := copy(buf, cast([]byte)s); + return cast(string)buf[:len]; +} + +format_uint :: proc(buf: []byte, u: u64, base: int) -> string { + using Int_Flag; + return format_bits(buf, u, base, false, digits, 0); +} +format_int :: proc(buf: []byte, i: i64, base: int) -> string { + return format_bits(buf, cast(u64)i, base, i < 0, digits, 0); +} +itoa :: proc(buf: []byte, i: int) -> string { + return format_int(buf, cast(i64)i, 10); +} + +format_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string { + return cast(string)generic_ftoa(buf, f, fmt, prec, bit_size); +} + + + + +Decimal_Slice :: struct { + d: []byte, + ndu: int, + dp: int, + neg: bool, +} + +Float_Info :: struct { + mantbits: uint, + expbits: uint, + bias: int, +} + +f32_info := Float_Info{23, 8, -127}; +f64_info := Float_Info{52, 11, -1023}; + + +generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> []byte { + bits: u64; + flt: ^Float_Info; + match bit_size { + case 32: + bits = cast(u64)transmute(u32)cast(f32)val; + flt = ^f32_info; + case 64: + bits = transmute(u64)val; + flt = ^f64_info; + default: + panic("strconv: invalid bit_size"); + } + + neg := bits>>(flt.expbits+flt.mantbits) != 0; + exp := cast(int)(bits>>flt.mantbits) & (1< []byte { + match fmt { + case 'f', 'F': + add_bytes :: proc(dst: ^[]byte, w: ^int, bytes: ...byte) { + for b in bytes { + if dst.count <= w^ { + break; + } + dst[w^] = b; + w^++; + } + } + + dst := buf[:]; + w := 0; + if neg { + add_bytes(^dst, ^w, '-'); + } else { + add_bytes(^dst, ^w, '+'); + } + + // integer, padded with zeros when needed + if digs.dp > 0 { + m := min(digs.ndu, digs.dp); + add_bytes(^dst, ^w, ...digs.d[:m]); + for ; m < digs.dp; m++ { + add_bytes(^dst, ^w, '0'); + } + } else { + add_bytes(^dst, ^w, '0'); + } + + // fractional part + if prec > 0 { + add_bytes(^dst, ^w, '.'); + for i in 0.. 2^(exp-mantbits) + log2(10) * (dp-nd) > exp-mantbits + log(2) >~ 0.332 + 332*(dp-nd) >= 100*(exp-mantbits) + */ + minexp := flt.bias+1; + if exp > minexp && 332*(d.dp-d.ndu) >= 100*(exp - cast(int)flt.mantbits) { + // Number is already its shortest + return; + } + + upper_: Decimal; upper: = ^upper_; + assign(upper, 2*mant - 1); + shift(upper, exp - cast(int)flt.mantbits - 1); + + mantlo: u64; + explo: int; + if mant > 1< string { + is_pow2 :: proc(x: i64) -> bool { + if (x <= 0) { + return false; + } + return x&(x-1) == 0; + } + + if base < 2 || base > MAX_BASE { + panic("strconv: illegal base passed to format_bits"); + } + + a: [65]byte; + i := a.count; + if neg { + u = -u; + } + + if is_pow2(cast(i64)base) { + b := cast(u64)base; + m := cast(uint)b - 1; + for u >= b { + i--; + a[i] = digits[cast(uint)u & m]; + u >>= b; + } + i--; + a[i] = digits[cast(uint)u]; + } else { + b := cast(u64)base; + for u >= b { + i--; + q := u / b; + a[i] = digits[cast(uint)(u-q*b)]; + u = q; + } + + i--; + a[i] = digits[cast(uint)u]; + } + + if flags&Int_Flag.PREFIX != 0 { + ok := true; + match base { + case 2: i--; a[i] = 'b'; + case 8: i--; a[i] = 'o'; + case 10: i--; a[i] = 'd'; + case 16: i--; a[i] = 'x'; + default: ok = false; + } + if ok { + i--; + a[i] = '0'; + } + } + + if neg { + i--; a[i] = '-'; + } else if flags&Int_Flag.PLUS != 0 { + i--; a[i] = '+'; + } else if flags&Int_Flag.SPACE != 0 { + i--; a[i] = ' '; + } + + + len := copy(buf, ...a[i:]); + return cast(string)buf[:len]; +} + diff --git a/core/strings.odin b/core/strings.odin index bfc16a184..7439fff32 100644 --- a/core/strings.odin +++ b/core/strings.odin @@ -9,7 +9,7 @@ to_odin_string :: proc(c: ^byte) -> string { s: string; s.data = c; for (c+s.count)^ != 0 { - s.count += 1; + s.count++; } return s; } diff --git a/core/sync.odin b/core/sync.odin index e346c60d4..3126d6882 100644 --- a/core/sync.odin +++ b/core/sync.odin @@ -52,7 +52,7 @@ mutex_lock :: proc(m: ^Mutex) { } } atomic.store(^m.owner, thread_id); - m.recursion += 1; + m.recursion++; } mutex_try_lock :: proc(m: ^Mutex) -> bool { thread_id := current_thread_id(); @@ -68,7 +68,7 @@ mutex_try_lock :: proc(m: ^Mutex) -> bool { } atomic.store(^m.owner, thread_id); } - m.recursion += 1; + m.recursion++; return true; } mutex_unlock :: proc(m: ^Mutex) { @@ -76,7 +76,7 @@ mutex_unlock :: proc(m: ^Mutex) { thread_id := current_thread_id(); assert(thread_id == atomic.load(^m.owner)); - m.recursion -= 1; + m.recursion--; recursion = m.recursion; if recursion == 0 { atomic.store(^m.owner, thread_id); diff --git a/core/utf8.odin b/core/utf8.odin index 8720e1f20..1b304fde0 100644 --- a/core/utf8.odin +++ b/core/utf8.odin @@ -92,7 +92,8 @@ encode_rune :: proc(r: rune) -> ([4]byte, int) { return buf, 4; } -decode_rune :: proc(s: string) -> (rune, int) { +decode_rune :: proc(s: string) -> (rune, int) #inline { return decode_rune(cast([]byte)s); } +decode_rune :: proc(s: []byte) -> (rune, int) { n := s.count; if n < 1 { return RUNE_ERROR, 0; @@ -130,6 +131,46 @@ decode_rune :: proc(s: string) -> (rune, int) { } + +decode_last_rune :: proc(s: string) -> (rune, int) #inline { return decode_last_rune(cast([]byte)s); } +decode_last_rune :: proc(s: []byte) -> (rune, int) { + r: rune; + size: int; + start, end, limit: int; + + end = s.count; + if end == 0 { + return RUNE_ERROR, 0; + } + start = end-1; + r = cast(rune)s[start]; + if r < RUNE_SELF { + return r, 1; + } + + + limit = max(end - UTF_MAX, 0); + + start--; + for start >= limit { + if rune_start(s[start]) { + break; + } + start--; + } + + start = max(start, 0); + r, size = decode_rune(s[start:end]); + if start+size != end { + return RUNE_ERROR, 1; + } + return r, size; +} + + + + + valid_rune :: proc(r: rune) -> bool { if r < 0 { return false; @@ -146,7 +187,7 @@ valid_string :: proc(s: string) -> bool { for i := 0; i < n; { si := s[i]; if si < RUNE_SELF { // ascii - i += 1; + i++; continue; } x := accept_sizes[si]; @@ -174,25 +215,28 @@ valid_string :: proc(s: string) -> bool { return true; } -rune_count :: proc(s: string) -> int { +rune_start :: proc(b: byte) -> bool #inline { return b&0xc0 != 0x80; } + +rune_count :: proc(s: string) -> int #inline { return rune_count(cast([]byte)s); } +rune_count :: proc(s: []byte) -> int { count := 0; n := s.count; for i := 0; i < n; { - defer count += 1; + defer count++; si := s[i]; if si < RUNE_SELF { // ascii - i += 1; + i++; continue; } x := accept_sizes[si]; if x == 0xf1 { - i += 1; + i++; continue; } size := cast(int)(x & 7); if i+size > n { - i += 1; + i++; continue; } ar := accept_ranges[x>>4]; diff --git a/src/check_expr.c b/src/check_expr.c index f075df688..aa5222d9d 100644 --- a/src/check_expr.c +++ b/src/check_expr.c @@ -197,11 +197,11 @@ i64 check_distance_between_types(Checker *c, Operand *operand, Type *type) { } } - // if (is_type_proc(dst)) { - // if (are_types_identical(src, dst)) { - // return 1; - // } - // } + if (is_type_proc(dst)) { + if (are_types_identical(src, dst)) { + return 3; + } + } if (is_type_any(dst)) { // NOTE(bill): Anything can cast to `Any` @@ -2827,6 +2827,16 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node, Type *type_h goto error; } } + + if (entity == NULL && + operand->type != NULL && is_type_untyped(operand->type) && is_type_string(operand->type)) { + String s = operand->value.value_string; + operand->mode = Addressing_Constant; + operand->value = make_exact_value_integer(s.len); + operand->type = t_untyped_integer; + return NULL; + } + if (entity == NULL) { gbString op_str = expr_to_string(op_expr); gbString type_str = type_to_string(operand->type); @@ -5445,9 +5455,10 @@ gbString write_expr_to_string(gbString str, AstNode *node) { case_ast_node(at, ArrayType, node); str = gb_string_appendc(str, "["); - if (at->count->kind == AstNode_UnaryExpr && - at->count->UnaryExpr.op.kind == Token_Hash) { - str = gb_string_appendc(str, "#"); + if (at->count != NULL && + at->count->kind == AstNode_UnaryExpr && + at->count->UnaryExpr.op.kind == Token_Ellipsis) { + str = gb_string_appendc(str, "..."); } else { str = write_expr_to_string(str, at->count); } diff --git a/src/check_stmt.c b/src/check_stmt.c index 90cb6ff61..9c0b0c187 100644 --- a/src/check_stmt.c +++ b/src/check_stmt.c @@ -421,6 +421,49 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { check_stmt(c, ts->stmt, flags); case_end; + case_ast_node(s, IncDecStmt, node); + TokenKind op = s->op.kind; + switch (op) { + case Token_Inc: op = Token_Add; break; + case Token_Dec: op = Token_Sub; break; + default: + error_node(node, "Invalid inc/dec operation"); + return; + } + + Operand x = {0}; + check_expr(c, &x, s->expr); + if (x.mode == Addressing_Invalid) { + return; + } + if (!is_type_integer(x.type) && !is_type_float(x.type)) { + gbString e = expr_to_string(s->expr); + gbString t = type_to_string(x.type); + error_node(node, "%s%.*s used on non-numeric type %s", e, LIT(s->op.string), t); + gb_string_free(t); + gb_string_free(e); + return; + } + AstNode *left = s->expr; + AstNode *right = gb_alloc_item(c->allocator, AstNode); + right->kind = AstNode_BasicLit; + right->BasicLit.pos = s->op.pos; + right->BasicLit.kind = Token_Integer; + right->BasicLit.string = str_lit("1"); + + AstNode *be = gb_alloc_item(c->allocator, AstNode); + be->kind = AstNode_BinaryExpr; + be->BinaryExpr.op = s->op; + be->BinaryExpr.op.kind = op; + be->BinaryExpr.left = left; + be->BinaryExpr.right = right; + check_binary_expr(c, &x, be); + if (x.mode == Addressing_Invalid) { + return; + } + check_assignment_variable(c, &x, left); + case_end; + case_ast_node(as, AssignStmt, node); switch (as->op.kind) { case Token_Eq: { @@ -591,8 +634,9 @@ void check_stmt_internal(Checker *c, AstNode *node, u32 flags) { if (fs->post != NULL) { check_stmt(c, fs->post, 0); - if (fs->post->kind != AstNode_AssignStmt) { - error_node(fs->post, "`for` statement post statement must be an assignment"); + if (fs->post->kind != AstNode_AssignStmt && + fs->post->kind != AstNode_IncDecStmt) { + error_node(fs->post, "`for` statement post statement must be a simple statement"); } } check_stmt(c, fs->body, new_flags); diff --git a/src/exact_value.c b/src/exact_value.c index 7c48d126b..57c8ae81d 100644 --- a/src/exact_value.c +++ b/src/exact_value.c @@ -274,8 +274,7 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) case ExactValue_Invalid: return v; case ExactValue_Integer: - i = v.value_integer; - i = ~i; + i = ~v.value_integer; break; default: goto failure; @@ -283,8 +282,10 @@ ExactValue exact_unary_operator_value(TokenKind op, ExactValue v, i32 precision) // NOTE(bill): unsigned integers will be negative and will need to be // limited to the types precision - if (precision > 0) + // IMPORTANT NOTE(bill): Max precision is 64 bits as that's how integers are stored + if (0 < precision && precision < 64) { i &= ~((~0ll)<module->allocator, src); i64 dz = type_size_of(proc->module->allocator, dst); irConvKind kind = irConv_trunc; - if (sz == dz) { + if (dz == sz) { // NOTE(bill): In LLVM, all integers are signed and rely upon 2's compliment // NOTE(bill): Copy the value just for type correctness kind = irConv_bitcast; } else if (dz > sz) { kind = irConv_zext; + + // TODO(bill): figure out the rules completely + bool ss = !is_type_unsigned(src); + bool ds = !is_type_unsigned(dst); + if (ss && ds) { + kind = irConv_sext; + } else if (ss) { + kind = irConv_sext; + } else { + kind = irConv_zext; + } } return ir_emit(proc, ir_make_instr_conv(proc, kind, value, src, dst)); @@ -4769,6 +4781,15 @@ void ir_build_stmt_internal(irProcedure *proc, AstNode *node) { ir_build_when_stmt(proc, ws); case_end; + case_ast_node(s, IncDecStmt, node); + TokenKind op = Token_Add; + if (s->op.kind == Token_Dec) { + op = Token_Sub; + } + irAddr addr = ir_build_addr(proc, s->expr); + ir_build_assign_op(proc, addr, v_one, op); + case_end; + case_ast_node(vd, ValueDecl, node); if (vd->is_var) { irModule *m = proc->module; diff --git a/src/parser.c b/src/parser.c index f14f37599..8349a8d6b 100644 --- a/src/parser.c +++ b/src/parser.c @@ -186,6 +186,10 @@ AST_NODE_KIND(_StmtBegin, "", i32) \ Token op; \ AstNodeArray lhs, rhs; \ }) \ + AST_NODE_KIND(IncDecStmt, "increment decrement statement", struct { \ + Token op; \ + AstNode *expr; \ + }) \ AST_NODE_KIND(_ComplexStmtBegin, "", i32) \ AST_NODE_KIND(BlockStmt, "block statement", struct { \ AstNodeArray stmts; \ @@ -467,6 +471,7 @@ Token ast_node_token(AstNode *node) { case AstNode_ExprStmt: return ast_node_token(node->ExprStmt.expr); case AstNode_TagStmt: return node->TagStmt.token; case AstNode_AssignStmt: return node->AssignStmt.op; + case AstNode_IncDecStmt: return ast_node_token(node->IncDecStmt.expr); case AstNode_BlockStmt: return node->BlockStmt.open; case AstNode_IfStmt: return node->IfStmt.token; case AstNode_WhenStmt: return node->WhenStmt.token; @@ -802,6 +807,16 @@ AstNode *ast_assign_stmt(AstFile *f, Token op, AstNodeArray lhs, AstNodeArray rh return result; } + +AstNode *ast_inc_dec_stmt(AstFile *f, Token op, AstNode *expr) { + AstNode *result = make_ast_node(f, AstNode_IncDecStmt); + result->IncDecStmt.op = op; + result->IncDecStmt.expr = expr; + return result; +} + + + AstNode *ast_block_stmt(AstFile *f, AstNodeArray stmts, Token open, Token close) { AstNode *result = make_ast_node(f, AstNode_BlockStmt); result->BlockStmt.stmts = stmts; @@ -2272,6 +2287,13 @@ AstNode *parse_simple_stmt(AstFile *f, bool in_stmt_ok) { return ast_bad_stmt(f, token, f->curr_token); } + switch (token.kind) { + case Token_Inc: + case Token_Dec: + next_token(f); + return ast_inc_dec_stmt(f, token, lhs.e[0]); + } + return ast_expr_stmt(f, lhs.e[0]); } diff --git a/src/tokenizer.c b/src/tokenizer.c index 68ab270be..dde3391a1 100644 --- a/src/tokenizer.c +++ b/src/tokenizer.c @@ -51,8 +51,8 @@ TOKEN_KIND(Token__AssignOpBegin, "_AssignOpBegin"), \ TOKEN_KIND(Token__AssignOpEnd, "_AssignOpEnd"), \ TOKEN_KIND(Token_ArrowRight, "->"), \ TOKEN_KIND(Token_ArrowLeft, "<-"), \ - TOKEN_KIND(Token_Increment, "++"), \ - TOKEN_KIND(Token_Decrement, "--"), \ + TOKEN_KIND(Token_Inc, "++"), \ + TOKEN_KIND(Token_Dec, "--"), \ \ TOKEN_KIND(Token__ComparisonBegin, "_ComparisonBegin"), \ TOKEN_KIND(Token_CmpEq, "=="), \ @@ -859,13 +859,13 @@ Token tokenizer_get_token(Tokenizer *t) { case '{': token.kind = Token_OpenBrace; break; case '}': token.kind = Token_CloseBrace; break; - case '*': token.kind = token_kind_variant2(t, Token_Mul, Token_MulEq); break; - case '%': token.kind = token_kind_variant2(t, Token_Mod, Token_ModEq); break; - case '=': token.kind = token_kind_variant2(t, Token_Eq, Token_CmpEq); break; - case '~': token.kind = token_kind_variant2(t, Token_Xor, Token_XorEq); break; - case '!': token.kind = token_kind_variant2(t, Token_Not, Token_NotEq); break; - case '+': token.kind = token_kind_variant3(t, Token_Add, Token_AddEq, '+', Token_Increment); break; - case '-': token.kind = token_kind_variant4(t, Token_Sub, Token_SubEq, '-', Token_Decrement, '>', Token_ArrowRight); break; + case '*': token.kind = token_kind_variant2(t, Token_Mul, Token_MulEq); break; + case '%': token.kind = token_kind_variant2(t, Token_Mod, Token_ModEq); break; + case '=': token.kind = token_kind_variant2(t, Token_Eq, Token_CmpEq); break; + case '~': token.kind = token_kind_variant2(t, Token_Xor, Token_XorEq); break; + case '!': token.kind = token_kind_variant2(t, Token_Not, Token_NotEq); break; + case '+': token.kind = token_kind_variant3(t, Token_Add, Token_AddEq, '+', Token_Inc); break; + case '-': token.kind = token_kind_variant4(t, Token_Sub, Token_SubEq, '-', Token_Dec, '>', Token_ArrowRight); break; case '/': { if (t->curr_rune == '/') { while (t->curr_rune != '\n' && t->curr_rune != GB_RUNE_EOF) {