++ -- statements; add strconv.odin (and replace some of the fmt procs); Fix ~ on 64 bit constants; Fix integer casts from smaller to larger size

This commit is contained in:
Ginger Bill
2017-02-26 00:44:26 +00:00
parent 67ed8a9a4a
commit c59f6b7d0b
17 changed files with 881 additions and 176 deletions

View File

@@ -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 {
/*

View File

@@ -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);

View File

@@ -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();

258
core/decimal.odin Normal file
View File

@@ -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) - 1;
for ; r < a.ndu; r++ {
c := cast(uint)a.d[r];
dig := n>>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;
}

View File

@@ -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..<prec {
val := cast(u64)(arg*mult);
buffer_write_byte(fi.buf, __DIGITS_LOWER[cast(u64)val]);
arg -= cast(f64)val / mult;
mult *= 10;
}
}
pad(fi, str[1:]);
}
if width > 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++;
}
}

View File

@@ -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;

View File

@@ -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--;
}

337
core/strconv.odin Normal file
View File

@@ -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<<flt.expbits - 1);
mant := bits & (cast(u64)1 << flt.mantbits - 1);
match exp {
case 1<<flt.expbits - 1:
s: string;
if mant != 0 {
s = "NaN";
} else if neg {
s = "-Inf";
} else {
s = "+Inf";
}
len := copy(buf, cast([]byte)s);
return buf[:len];
case 0: // denormalized
exp++;
default:
mant |= cast(u64)1 << flt.mantbits;
}
exp += flt.bias;
d_: Decimal;
d := ^d_;
assign(d, mant);
shift(d, exp - cast(int)flt.mantbits);
digs: Decimal_Slice;
shortest := prec < 0;
if shortest {
round_shortest(d, mant, exp, flt);
digs = Decimal_Slice{d = d.d[:], ndu = d.ndu, dp = d.dp};
match fmt {
case 'e', 'E':
prec = digs.ndu-1;
case 'f', 'F':
prec = max(digs.ndu-digs.dp, 0);
case 'g', 'G':
prec = digs.ndu;
}
} else {
match fmt {
case 'e', 'E': round(d, prec+1);
case 'f', 'F': round(d, d.dp+prec);
case 'g', 'G':
if prec == 0 {
prec = 1;
}
round(d, prec);
}
digs = Decimal_Slice{d = d.d[:], ndu = d.ndu, dp = d.dp};
}
return format_digits(buf, shortest, neg, digs, prec, fmt);
}
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, prec: int, fmt: byte) -> []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..<prec {
c: byte = '0';
if j := digs.dp + i; 0 <= j && j < digs.ndu {
c = digs.d[j];
}
add_bytes(^dst, ^w, c);
}
}
return buf[:w];
case 'e', 'E':
return nil; // TODO
case 'g', 'G':
return nil; // TODO
}
c: [2]byte;
c[0] = '%';
c[1] = fmt;
len := copy(buf, ...c[:]);
return buf[:len];
}
round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
if mant == 0 { // If mantissa is zero, the number is zero
d.ndu = 0;
return;
}
/*
10^(dp-nd) > 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<<flt.mantbits || exp == minexp {
mantlo = mant-1;
explo = exp;
} else {
mantlo = 2*mant - 1;
explo = exp-1;
}
lower_: Decimal; lower: = ^lower_;
assign(lower, 2*mantlo + 1);
shift(lower, explo - cast(int)flt.mantbits - 1);
inclusive := mant%2 == 0;
for i in 0..<d.ndu {
l: byte = '0'; // lower digit
if i < lower.ndu {
l = lower.d[i];
}
m := d.d[i]; // middle digit
u: byte = '0'; // upper digit
if i < upper.ndu {
u = upper.d[i];
}
ok_round_down := l != m || inclusive && i+1 == lower.ndu;
ok_round_up := m != u && (inclusive || m+1 < u || i+1 < upper.ndu);
if (ok_round_down && ok_round_up) {
round(d, i+1);
return;
}
if (ok_round_down) {
round_down(d, i+1);
return;
}
if (ok_round_up) {
round_up(d, i+1);
return;
}
}
}
MAX_BASE :: 32;
immutable digits := "0123456789abcdefghijklmnopqrstuvwxyz";
format_bits :: proc(buf: []byte, u: u64, base: int, neg: bool, digits: string, flags: Int_Flag) -> 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];
}

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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];

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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)<<precision);
}
return make_exact_value_integer(i);
} break;

View File

@@ -233,6 +233,7 @@ struct irProcedure {
#define IR_CONV_KINDS \
IR_CONV_KIND(trunc) \
IR_CONV_KIND(zext) \
IR_CONV_KIND(sext) \
IR_CONV_KIND(fptrunc) \
IR_CONV_KIND(fpext) \
IR_CONV_KIND(fptoui) \
@@ -2240,12 +2241,23 @@ irValue *ir_emit_conv(irProcedure *proc, irValue *value, Type *t) {
i64 sz = type_size_of(proc->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;

View File

@@ -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]);
}

View File

@@ -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) {