mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-06 18:54:12 +00:00
++ -- 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:
@@ -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 {
|
||||
/*
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
258
core/decimal.odin
Normal 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;
|
||||
}
|
||||
214
core/fmt.odin
214
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..<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++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
337
core/strconv.odin
Normal 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];
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
23
src/ir.c
23
src/ir.c
@@ -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;
|
||||
|
||||
22
src/parser.c
22
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]);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user