Merge branch 'master' into master

This commit is contained in:
Brendan Punsky
2019-03-18 17:22:57 -04:00
committed by GitHub
19 changed files with 691 additions and 244 deletions
+17 -8
View File
@@ -130,10 +130,15 @@ shift_right :: proc(a: ^Decimal, k: uint) {
}
shift_left :: proc(a: ^Decimal, k: uint) {
delta := int(k/4);
// NOTE(bill): used to determine buffer size required for the decimal from the binary shift
// 'k' means `1<<k` == `2^k` which equates to roundup(k*log10(2)) digits required
log10_2 :: 0.301029995663981195213738894724493026768189881462108541310;
capacity := int(f64(k)*log10_2 + 1);
r := a.count; // read index
w := a.count+delta; // write index
r := a.count; // read index
w := a.count+capacity; // write index
d := len(a.digits);
n: uint;
for r -= 1; r >= 0; r -= 1 {
@@ -141,7 +146,7 @@ shift_left :: proc(a: ^Decimal, k: uint) {
quo := n/10;
rem := n - 10*quo;
w -= 1;
if w < len(a.digits) {
if w < d {
a.digits[w] = byte('0' + rem);
} else if rem != 0 {
a.trunc = true;
@@ -153,7 +158,7 @@ shift_left :: proc(a: ^Decimal, k: uint) {
quo := n/10;
rem := n - 10*quo;
w -= 1;
if 0 <= w && w < len(a.digits) {
if w < d {
a.digits[w] = byte('0' + rem);
} else if rem != 0 {
a.trunc = true;
@@ -161,9 +166,12 @@ shift_left :: proc(a: ^Decimal, k: uint) {
n = quo;
}
a.count += delta;
a.count = min(a.count, len(a.digits));
a.decimal_point += delta;
// NOTE(bill): Remove unused buffer size
assert(w >= 0);
capacity -= w;
a.count = min(a.count+capacity, d);
a.decimal_point += capacity;
trim(a);
}
@@ -253,3 +261,4 @@ rounded_integer :: proc(a: ^Decimal) -> u64 {
}
return n;
}
+18 -20
View File
@@ -2,8 +2,6 @@ package cel;
import "core:fmt"
import "core:strconv"
import "core:os"
import "core:mem"
import "core:unicode/utf8"
import "core:strings"
@@ -34,7 +32,7 @@ Parser :: struct {
print_value :: proc(value: Value, pretty := true, indent := 0) {
print_indent :: proc(indent: int) {
for i in 0..indent-1 do fmt.print("\t");
for _ in 0..indent-1 do fmt.print("\t");
}
switch v in value {
@@ -62,16 +60,16 @@ print_value :: proc(value: Value, pretty := true, indent := 0) {
if pretty do fmt.println();
i := 0;
for name, value in v {
for name, val in v {
if pretty {
print_indent(indent+1);
fmt.printf("%s = ", name);
print_value(value, pretty, indent+1);
print_value(val, pretty, indent+1);
fmt.println(",");
} else {
if i > 0 do fmt.print(", ");
fmt.printf("%s = ", name);
print_value(value, pretty, indent+1);
print_value(val, pretty, indent+1);
i += 1;
}
}
@@ -155,7 +153,7 @@ destroy :: proc(p: ^Parser) {
delete(v);
case Dict:
for key, value in v do destroy_value(value);
for _, dv in v do destroy_value(dv);
delete(v);
}
}
@@ -201,11 +199,12 @@ unquote_char :: proc(s: string, quote: byte) -> (r: rune, multiple_bytes: bool,
}
return -1;
}
w: int;
if s[0] == quote && quote == '"' {
return;
} else if s[0] >= 0x80 {
r, w := utf8.decode_rune_in_string(s);
r, w = utf8.decode_rune_in_string(s);
return r, true, s[w:], true;
} else if s[0] != '\\' {
return rune(s[0]), false, s[1:], true;
@@ -291,7 +290,6 @@ unquote_string :: proc(p: ^Parser, t: Token) -> (string, bool) {
return t.lit, true;
}
s := t.lit;
n := len(s);
quote := '"';
if s == `""` {
@@ -442,7 +440,7 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
case Kind.Open_Paren:
expect_token(p, Kind.Open_Paren);
expr, pos := parse_expr(p);
expr, _ := parse_expr(p);
expect_token(p, Kind.Close_Paren);
return expr, tok.pos;
@@ -451,7 +449,7 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
elems := make([dynamic]Value, 0, 4);
for p.curr_token.kind != Kind.Close_Bracket &&
p.curr_token.kind != Kind.EOF {
elem, pos := parse_expr(p);
elem, _ := parse_expr(p);
append(&elems, elem);
if p.curr_token.kind == Kind.Semicolon && p.curr_token.lit == "\n" {
@@ -481,9 +479,9 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
name, ok := unquote_string(p, name_tok);
if !ok do error(p, tok.pos, "Unable to unquote string");
expect_token(p, Kind.Assign);
elem, pos := parse_expr(p);
elem, _ := parse_expr(p);
if _, ok := dict[name]; ok {
if _, ok2 := dict[name]; ok2 {
error(p, name_tok.pos, "Previous declaration of %s in this scope", name);
} else {
dict[name] = elem;
@@ -533,9 +531,9 @@ parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
}
case Kind.Open_Bracket:
open := expect_token(p, Kind.Open_Bracket);
expect_token(p, Kind.Open_Bracket);
index, index_pos := parse_expr(p);
close := expect_token(p, Kind.Close_Bracket);
expect_token(p, Kind.Close_Bracket);
switch a in operand {
@@ -613,7 +611,7 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
case Kind.Not:
next_token(p);
expr, pos := parse_unary_expr(p);
expr, _ := parse_unary_expr(p);
if v, ok := expr.(bool); ok {
return !v, op.pos;
}
@@ -757,9 +755,9 @@ parse_binary_expr :: proc(p: ^Parser, prec_in: int) -> (Value, Pos) {
if op.kind == Kind.Question {
cond := expr;
x, x_pos := parse_expr(p);
x, _ := parse_expr(p);
expect_token(p, Kind.Colon);
y, y_pos := parse_expr(p);
y, _ := parse_expr(p);
if t, ok := cond.(bool); ok {
expr = t ? x : y;
@@ -824,9 +822,9 @@ parse_assignment :: proc(p: ^Parser) -> bool {
expect_token(p, Kind.Assign);
name, ok := unquote_string(p, tok);
if !ok do error(p, tok.pos, "Unable to unquote string");
expr, pos := parse_expr(p);
expr, _ := parse_expr(p);
d := top_dict(p);
if _, ok := d[name]; ok {
if _, ok2 := d[name]; ok2 {
error(p, tok.pos, "Previous declaration of %s", name);
} else {
d[name] = expr;
+5 -5
View File
@@ -322,7 +322,7 @@ scan_number :: proc(t: ^Tokenizer, seen_decimal_point: bool) -> (Kind, string) {
}
if t.curr_rune == '0' {
offset := t.offset;
offset = t.offset;
advance_to_next_rune(t);
switch t.curr_rune {
case 'b', 'B':
@@ -403,17 +403,17 @@ scan :: proc(t: ^Tokenizer) -> Token {
quote := r;
tok = String;
for {
r := t.curr_rune;
if r == '\n' || r < 0 {
this_r := t.curr_rune;
if this_r == '\n' || r < 0 {
token_error(t, "String literal not terminated");
break;
}
advance_to_next_rune(t);
if r == quote {
if this_r == quote {
break;
}
// TODO(bill); Handle properly
if r == '\\' && t.curr_rune == quote {
if this_r == '\\' && t.curr_rune == quote {
advance_to_next_rune(t);
}
}
+125 -21
View File
@@ -10,7 +10,7 @@ import "core:strconv"
import "core:strings"
@(private)
@private
DEFAULT_BUFFER_SIZE :: 1<<12;
Info :: struct {
@@ -31,6 +31,7 @@ Info :: struct {
buf: ^strings.Builder,
arg: any, // Temporary
record_level: int,
}
fprint :: proc(fd: os.Handle, args: ..any) -> int {
@@ -551,9 +552,6 @@ _pad :: proc(fi: ^Info, s: string) {
fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
switch verb {
// case 'e', 'E', 'f', 'F', 'g', 'G', 'v':
// case 'f', 'F', 'v':
case 'f', 'F', 'v':
prec: int = 3;
if fi.prec_set do prec = fi.prec;
@@ -588,6 +586,59 @@ fmt_float :: proc(fi: ^Info, v: f64, bit_size: int, verb: rune) {
_pad(fi, str[1:]);
}
case 'e', 'E':
prec: int = 3;
if fi.prec_set do prec = fi.prec;
buf: [386]byte;
str := strconv.append_float(buf[1:], v, 'e', prec, bit_size);
str = string(buf[:len(str)+1]);
if str[1] == '+' || str[1] == '-' {
str = str[1:];
} else {
str[0] = '+';
}
if fi.space && !fi.plus && str[0] == '+' {
str[0] = ' ';
}
if len(str) > 1 && (str[1] == 'N' || str[1] == 'I') {
strings.write_string(fi.buf, str);
return;
}
if fi.plus || str[0] != '+' {
if fi.zero && fi.width_set && fi.width > len(str) {
strings.write_byte(fi.buf, str[0]);
fmt_write_padding(fi, fi.width - len(str));
strings.write_string(fi.buf, str[1:]);
} else {
_pad(fi, str);
}
} else {
_pad(fi, str[1:]);
}
case 'h', 'H':
prev_fi := fi^;
defer fi^ = prev_fi;
fi.hash = false;
fi.width = bit_size;
fi.zero = true;
fi.plus = false;
u: u64;
switch bit_size {
case 32: u = u64(transmute(u32)f32(v));
case 64: u = transmute(u64)v;
case: panic("Unhandled float size");
}
strings.write_string(fi.buf, "0h");
_fmt_int(fi, u, 16, false, bit_size, verb == 'h' ? __DIGITS_LOWER : __DIGITS_UPPER);
case:
fmt_bad_verb(fi, verb);
}
@@ -623,13 +674,20 @@ fmt_cstring :: proc(fi: ^Info, s: cstring, verb: rune) {
}
fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
u := u64(uintptr(p));
switch verb {
case 'p', 'v':
u := u64(uintptr(p));
if !fi.hash || verb == 'v' {
strings.write_string(fi.buf, "0x");
}
_fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER);
case 'b': _fmt_int(fi, u, 2, false, 8*size_of(rawptr), __DIGITS_UPPER);
case 'o': _fmt_int(fi, u, 8, false, 8*size_of(rawptr), __DIGITS_UPPER);
case 'd': _fmt_int(fi, u, 10, false, 8*size_of(rawptr), __DIGITS_UPPER);
case 'x': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER);
case 'X': _fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER);
case:
fmt_bad_verb(fi, verb);
}
@@ -982,6 +1040,43 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
if v.id == typeid_of(^runtime.Type_Info) {
write_type(fi.buf, (^^runtime.Type_Info)(v.data)^);
} else {
if verb != 'p' {
ptr := (^rawptr)(v.data)^;
a := any{ptr, info.elem.id};
elem := runtime.type_info_base(info.elem);
if elem != nil do switch e in elem.variant {
case runtime.Type_Info_Array,
runtime.Type_Info_Slice,
runtime.Type_Info_Dynamic_Array,
runtime.Type_Info_Map:
if ptr == nil {
strings.write_string(fi.buf, "<nil>");
return;
}
if fi.record_level < 1 {
fi.record_level += 1;
defer fi.record_level -= 1;
strings.write_byte(fi.buf, '&');
fmt_value(fi, a, verb);
return;
}
case runtime.Type_Info_Struct,
runtime.Type_Info_Union:
if ptr == nil {
strings.write_string(fi.buf, "<nil>");
return;
}
if fi.record_level < 1 {
fi.record_level += 1;
defer fi.record_level -= 1;
strings.write_byte(fi.buf, '&');
fmt_value(fi, a, verb);
return;
}
}
}
fmt_pointer(fi, (^rawptr)(v.data)^, verb);
}
@@ -996,14 +1091,19 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
}
case runtime.Type_Info_Dynamic_Array:
strings.write_byte(fi.buf, '[');
defer strings.write_byte(fi.buf, ']');
array := cast(^mem.Raw_Dynamic_Array)v.data;
for i in 0..array.len-1 {
if i > 0 do strings.write_string(fi.buf, ", ");
if verb == 'p' {
slice := cast(^mem.Raw_Dynamic_Array)v.data;
fmt_pointer(fi, slice.data, 'p');
} else {
strings.write_byte(fi.buf, '[');
defer strings.write_byte(fi.buf, ']');
array := cast(^mem.Raw_Dynamic_Array)v.data;
for i in 0..array.len-1 {
if i > 0 do strings.write_string(fi.buf, ", ");
data := uintptr(array.data) + uintptr(i*info.elem_size);
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
data := uintptr(array.data) + uintptr(i*info.elem_size);
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
}
}
case runtime.Type_Info_Simd_Vector:
@@ -1021,16 +1121,20 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
case runtime.Type_Info_Slice:
strings.write_byte(fi.buf, '[');
defer strings.write_byte(fi.buf, ']');
slice := cast(^mem.Raw_Slice)v.data;
for i in 0..slice.len-1 {
if i > 0 do strings.write_string(fi.buf, ", ");
if verb == 'p' {
slice := cast(^mem.Raw_Slice)v.data;
fmt_pointer(fi, slice.data, 'p');
} else {
strings.write_byte(fi.buf, '[');
defer strings.write_byte(fi.buf, ']');
slice := cast(^mem.Raw_Slice)v.data;
for i in 0..slice.len-1 {
if i > 0 do strings.write_string(fi.buf, ", ");
data := uintptr(slice.data) + uintptr(i*info.elem_size);
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
data := uintptr(slice.data) + uintptr(i*info.elem_size);
fmt_arg(fi, any{rawptr(data), info.elem.id}, verb);
}
}
case runtime.Type_Info_Map:
if verb != 'v' {
fmt_bad_verb(fi, verb);
@@ -1161,7 +1265,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
fmt_complex :: proc(fi: ^Info, c: complex128, bits: int, verb: rune) {
switch verb {
case 'f', 'F', 'v':
case 'f', 'F', 'v', 'h', 'H':
r, i := real(c), imag(c);
fmt_float(fi, r, bits/2, verb);
if !fi.plus && i >= 0 {
+6 -6
View File
@@ -212,7 +212,7 @@ get_last_error :: proc() -> int {
}
open :: proc(path: string, flags: int = O_RDONLY, mode: int = 0) -> (Handle, Errno) {
cstr := strings.new_cstring(path);
cstr := strings.clone_to_cstring(path);
handle := _unix_open(cstr, flags, mode);
delete(cstr);
if handle == -1 {
@@ -289,7 +289,7 @@ last_write_time_by_name :: proc(name: string) -> (File_Time, Errno) {
}
stat :: inline proc(path: string) -> (Stat, Errno) {
cstr := strings.new_cstring(path);
cstr := strings.clone_to_cstring(path);
defer delete(cstr);
s: Stat;
@@ -310,7 +310,7 @@ fstat :: inline proc(fd: Handle) -> (Stat, Errno) {
}
access :: inline proc(path: string, mask: int) -> (bool, Errno) {
cstr := strings.new_cstring(path);
cstr := strings.clone_to_cstring(path);
defer delete(cstr);
result := _unix_access(cstr, mask);
if result == -1 {
@@ -333,7 +333,7 @@ heap_free :: proc(ptr: rawptr) {
}
getenv :: proc(name: string) -> (string, bool) {
path_str := strings.new_cstring(name);
path_str := strings.clone_to_cstring(name);
defer delete(path_str);
cstr := _unix_getenv(path_str);
if cstr == nil {
@@ -370,14 +370,14 @@ current_thread_id :: proc "contextless" () -> int {
}
dlopen :: inline proc(filename: string, flags: int) -> rawptr {
cstr := strings.new_cstring(filename);
cstr := strings.clone_to_cstring(filename);
defer delete(cstr);
handle := _unix_dlopen(cstr, flags);
return handle;
}
dlsym :: inline proc(handle: rawptr, symbol: string) -> rawptr {
assert(handle != nil);
cstr := strings.new_cstring(symbol);
cstr := strings.clone_to_cstring(symbol);
defer delete(cstr);
proc_handle := _unix_dlsym(handle, cstr);
return proc_handle;
+75 -15
View File
@@ -213,28 +213,28 @@ append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> str
DecimalSlice :: struct {
Decimal_Slice :: struct {
digits: []byte,
count: int,
decimal_point: int,
neg: bool,
}
FloatInfo :: struct {
Float_Info :: struct {
mantbits: uint,
expbits: uint,
bias: int,
}
_f16_info := FloatInfo{10, 5, -15};
_f32_info := FloatInfo{23, 8, -127};
_f64_info := FloatInfo{52, 11, -1023};
_f16_info := Float_Info{10, 5, -15};
_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: ^FloatInfo;
flt: ^Float_Info;
switch bit_size {
case 32:
bits = u64(transmute(u32)f32(val));
@@ -276,11 +276,11 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
d := &d_;
assign(d, mant);
shift(d, exp - int(flt.mantbits));
digs: DecimalSlice;
digs: Decimal_Slice;
shortest := prec < 0;
if shortest {
round_shortest(d, mant, exp, flt);
digs = DecimalSlice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
digs = Decimal_Slice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
switch fmt {
case 'e', 'E': prec = digs.count-1;
case 'f', 'F': prec = max(digs.count-digs.decimal_point, 0);
@@ -297,14 +297,14 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, prec, bit_size: int) -> [
round(d, prec);
}
digs = DecimalSlice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
digs = Decimal_Slice{digits = d.digits[:], count = d.count, decimal_point = d.decimal_point};
}
return format_digits(buf, shortest, neg, digs, prec, fmt);
}
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: DecimalSlice, prec: int, fmt: byte) -> []byte {
format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, prec: int, fmt: byte) -> []byte {
Buffer :: struct {
b: []byte,
n: int,
@@ -347,12 +347,72 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: DecimalSlice
return to_bytes(b);
case 'e', 'E':
panic("strconv: e/E float printing is not yet supported");
return to_bytes(b); // TODO
add_bytes(&b, neg ? '-' : '+');
ch := byte('0');
if digs.count != 0 {
ch = digs.digits[0];
}
add_bytes(&b, ch);
if prec > 0 {
add_bytes(&b, '.');
i := 1;
m := min(digs.count, prec+1);
if i < m {
add_bytes(&b, ..digs.digits[i:m]);
i = m;
}
for ; i <= prec; i += 1 {
add_bytes(&b, '0');
}
}
add_bytes(&b, fmt);
exp := digs.decimal_point-1;
if digs.count == 0 {
// Zero has exponent of 0
exp = 0;
}
ch = '+';
if exp < 0 {
ch = '-';
exp = -exp;
}
add_bytes(&b, ch);
switch {
case exp < 10: add_bytes(&b, '0', byte(exp)+'0'); // add prefix 0
case exp < 100: add_bytes(&b, byte(exp/10)+'0', byte(exp%10)+'0');
case: add_bytes(&b, byte(exp/100)+'0', byte(exp/10)%10+'0', byte(exp%10)+'0');
}
return to_bytes(b);
case 'g', 'G':
panic("strconv: g/G float printing is not yet supported");
return to_bytes(b); // TODO
eprec := prec;
if eprec > digs.count && digs.count >= digs.decimal_point {
eprec = digs.count;
}
if shortest {
eprec = 6;
}
exp := digs.decimal_point - 1;
if exp < -4 || exp >= eprec {
if prec > digs.count {
prec = digs.count;
}
return format_digits(buf, shortest, neg, digs, prec-1, fmt+'e'-'g'); // keep the same case
}
if prec > digs.decimal_point {
prec = digs.count;
}
return format_digits(buf, shortest, neg, digs, max(prec-digs.decimal_point, 0), 'f');
case:
add_bytes(&b, '%', fmt);
@@ -362,7 +422,7 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: DecimalSlice
}
round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^FloatInfo) {
round_shortest :: proc(d: ^Decimal, mant: u64, exp: int, flt: ^Float_Info) {
if mant == 0 { // If mantissa is zero, the number is zero
d.count = 0;
return;
+4
View File
@@ -17,6 +17,10 @@ destroy_builder :: proc(b: ^Builder) {
clear(&b.buf);
}
grow_builder :: proc(b: ^Builder, cap: int) {
reserve(&b.buf, cap);
}
builder_from_slice :: proc(backing: []byte) -> Builder {
s := transmute(mem.Raw_Slice)backing;
d := mem.Raw_Dynamic_Array{
+200 -1
View File
@@ -3,6 +3,21 @@ package strings
import "core:mem"
import "core:unicode/utf8"
clone :: proc(s: string, allocator := context.allocator) -> string {
c := make([]byte, len(s)+1, allocator);
copy(c, cast([]byte)s);
c[len(s)] = 0;
return string(c[:len(s)]);
}
clone_to_cstring :: proc(s: string, allocator := context.allocator) -> cstring {
c := make([]byte, len(s)+1, allocator);
copy(c, cast([]byte)s);
c[len(s)] = 0;
return cstring(&c[0]);
}
@(deprecated="Please use 'strings.clone'")
new_string :: proc(s: string, allocator := context.allocator) -> string {
c := make([]byte, len(s)+1, allocator);
copy(c, cast([]byte)s);
@@ -10,6 +25,7 @@ new_string :: proc(s: string, allocator := context.allocator) -> string {
return string(c[:len(s)]);
}
@(deprecated="Please use 'strings.clone_to_cstring'")
new_cstring :: proc(s: string, allocator := context.allocator) -> cstring {
c := make([]byte, len(s)+1, allocator);
copy(c, cast([]byte)s);
@@ -46,6 +62,10 @@ contains_any :: proc(s, chars: string) -> bool {
}
rune_count :: proc(s: string) -> int {
return utf8.rune_count_in_string(s);
}
equal_fold :: proc(s, t: string) -> bool {
loop: for s != "" && t != "" {
@@ -209,7 +229,7 @@ last_index_any :: proc(s, chars: string) -> int {
count :: proc(s, substr: string) -> int {
if len(substr) == 0 { // special case
return utf8.rune_count_in_string(s) + 1;
return rune_count(s) + 1;
}
if len(substr) == 1 {
c := substr[0];
@@ -642,3 +662,182 @@ split_multi :: proc(s: string, substrs: []string, skip_empty := false, allocator
return buf;
}
// scrub scruvs invalid utf-8 characters and replaces them with the replacement string
// Adjacent invalid bytes are only replaced once
scrub :: proc(str: string, replacement: string, allocator := context.allocator) -> string {
b := make_builder(allocator);;
grow_builder(&b, len(str));
has_error := false;
cursor := 0;
origin := str;
for len(str) > 0 {
r, w := utf8.decode_rune_in_string(str);
if r == utf8.RUNE_ERROR {
if !has_error {
has_error = true;
write_string(&b, origin[:cursor]);
}
} else if has_error {
has_error = false;
write_string(&b, replacement);
origin = origin[cursor:];
cursor = 0;
}
cursor += w;
str = str[w:];
}
return to_string(b);
}
reverse :: proc(str: string, allocator := context.allocator) -> string {
n := len(str);
buf := make([]byte, n);
i := 0;
for len(str) > 0 {
_, w := utf8.decode_rune_in_string(str);
copy(buf[i:], cast([]byte)str[:w]);
str = str[w:];
}
return string(buf);
}
expand_tabs :: proc(str: string, tab_size: int, allocator := context.allocator) -> string {
if tab_size <= 0 {
panic("tab size must be positive");
}
if str == "" {
return "";
}
b := make_builder(allocator);
column: int;
for len(str) > 0 {
r, w := utf8.decode_rune_in_string(str);
if r == '\t' {
expand := tab_size - column%tab_size;
for i := 0; i < expand; i += 1 {
write_byte(&b, ' ');
}
column += expand;
} else {
if r == '\n' {
column = 0;
} else {
column += w;
}
write_rune(&b, r);
}
str = str[w:];
}
return to_string(b);
}
partition :: proc(str, sep: string) -> (head, match, tail: string) {
i := index(str, sep);
if i == -1 {
head = str;
return;
}
head = str[:i];
match = str[i:i+len(sep)];
tail = str[i+len(sep):];
return;
}
center_justify :: centre_justify; // NOTE(bill): Because Americans exist
// centre_justify returns a string with a pad string at boths sides if the str's rune length is smaller than length
centre_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
n := rune_count(str);
if n >= length || pad == "" {
return clone(str, allocator);
}
remains := length-1;
pad_len := rune_count(pad);
b := make_builder(allocator);
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
write_pad_string(&b, pad, pad_len, remains/2);
write_string(&b, str);
write_pad_string(&b, pad, pad_len, (remains+1)/2);
return to_string(b);
}
// left_justify returns a string with a pad string at left side if the str's rune length is smaller than length
left_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
n := rune_count(str);
if n >= length || pad == "" {
return clone(str, allocator);
}
remains := length-1;
pad_len := rune_count(pad);
b := make_builder(allocator);
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
write_string(&b, str);
write_pad_string(&b, pad, pad_len, remains);
return to_string(b);
}
// right_justify returns a string with a pad string at right side if the str's rune length is smaller than length
right_justify :: proc(str: string, length: int, pad: string, allocator := context.allocator) -> string {
n := rune_count(str);
if n >= length || pad == "" {
return clone(str, allocator);
}
remains := length-1;
pad_len := rune_count(pad);
b := make_builder(allocator);
grow_builder(&b, len(str) + (remains/pad_len + 1)*len(pad));
write_pad_string(&b, pad, pad_len, remains);
write_string(&b, str);
return to_string(b);
}
@private
write_pad_string :: proc(b: ^Builder, pad: string, pad_len, remains: int) {
repeats := remains / pad_len;
for i := 0; i < repeats; i += 1 {
write_string(b, pad);
}
remains = remains % pad_len;
if remains != 0 do for i := 0; i < remains; i += 1 {
r, w := utf8.decode_rune_in_string(pad);
write_rune(b, r);
pad = pad[w:];
}
}
+67 -79
View File
@@ -11,92 +11,86 @@ Ordering :: enum {
}
strongest_failure_ordering :: inline proc "contextless" (order: Ordering) -> Ordering {
using Ordering;
#complete switch order {
case Relaxed: return Relaxed;
case Release: return Relaxed;
case Acquire: return Acquire;
case Acquire_Release: return Acquire;
case Sequentially_Consistent: return Sequentially_Consistent;
case .Relaxed: return .Relaxed;
case .Release: return .Relaxed;
case .Acquire: return .Acquire;
case .Acquire_Release: return .Acquire;
case .Sequentially_Consistent: return .Sequentially_Consistent;
}
return Relaxed;
return .Relaxed;
}
fence :: inline proc "contextless" (order: Ordering) {
using Ordering;
#complete switch order {
case Relaxed: panic("there is no such thing as a relaxed fence");
case Release: intrinsics.atomic_fence_rel();
case Acquire: intrinsics.atomic_fence_acq();
case Acquire_Release: intrinsics.atomic_fence_acqrel();
case Sequentially_Consistent: intrinsics.atomic_fence();
case .Relaxed: panic("there is no such thing as a relaxed fence");
case .Release: intrinsics.atomic_fence_rel();
case .Acquire: intrinsics.atomic_fence_acq();
case .Acquire_Release: intrinsics.atomic_fence_acqrel();
case .Sequentially_Consistent: intrinsics.atomic_fence();
case: panic("unknown order");
}
}
atomic_store :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) {
using Ordering;
#complete switch order {
case Relaxed: intrinsics.atomic_store_relaxed(dst, val);
case Release: intrinsics.atomic_store_rel(dst, val);
case Sequentially_Consistent: intrinsics.atomic_store(dst, val);
case Acquire: panic("there is not such thing as an acquire store");
case Acquire_Release: panic("there is not such thing as an acquire/release store");
case .Relaxed: intrinsics.atomic_store_relaxed(dst, val);
case .Release: intrinsics.atomic_store_rel(dst, val);
case .Sequentially_Consistent: intrinsics.atomic_store(dst, val);
case .Acquire: panic("there is not such thing as an acquire store");
case .Acquire_Release: panic("there is not such thing as an acquire/release store");
case: panic("unknown order");
}
}
atomic_load :: inline proc "contextless" (dst: ^$T, order: Ordering) -> T {
using Ordering;
#complete switch order {
case Relaxed: return intrinsics.atomic_load_relaxed(dst);
case Acquire: return intrinsics.atomic_load_acq(dst);
case Sequentially_Consistent: return intrinsics.atomic_load(dst);
case Release: panic("there is no such thing as a release load");
case Acquire_Release: panic("there is no such thing as an acquire/release load");
case .Relaxed: return intrinsics.atomic_load_relaxed(dst);
case .Acquire: return intrinsics.atomic_load_acq(dst);
case .Sequentially_Consistent: return intrinsics.atomic_load(dst);
case .Release: panic("there is no such thing as a release load");
case .Acquire_Release: panic("there is no such thing as an acquire/release load");
}
panic("unknown order");
return T{};
}
atomic_swap :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
using Ordering;
#complete switch order {
case Relaxed: return intrinsics.atomic_xchg_relaxed(dst, val);
case Release: return intrinsics.atomic_xchg_rel(dst, val);
case Acquire: return intrinsics.atomic_xchg_acq(dst, val);
case Acquire_Release: return intrinsics.atomic_xchg_acqrel(dst, val);
case Sequentially_Consistent: return intrinsics.atomic_xchg(dst, val);
case .Relaxed: return intrinsics.atomic_xchg_relaxed(dst, val);
case .Release: return intrinsics.atomic_xchg_rel(dst, val);
case .Acquire: return intrinsics.atomic_xchg_acq(dst, val);
case .Acquire_Release: return intrinsics.atomic_xchg_acqrel(dst, val);
case .Sequentially_Consistent: return intrinsics.atomic_xchg(dst, val);
}
panic("unknown order");
return T{};
}
atomic_compare_exchange :: inline proc "contextless" (dst: ^$T, old, new: T, success, failure: Ordering) -> (val: T, ok: bool) {
using Ordering;
switch failure {
case Relaxed:
case .Relaxed:
switch success {
case Relaxed: return intrinsics.atomic_cxchg_relaxed(dst, old, new);
case Acquire: return intrinsics.atomic_cxchg_acq_failrelaxed(dst, old, new);
case Acquire_Release: return intrinsics.atomic_cxchg_acqrel_failrelaxed(dst, old, new);
case Sequentially_Consistent: return intrinsics.atomic_cxchg_failrelaxed(dst, old, new);
case .Relaxed: return intrinsics.atomic_cxchg_relaxed(dst, old, new);
case .Acquire: return intrinsics.atomic_cxchg_acq_failrelaxed(dst, old, new);
case .Acquire_Release: return intrinsics.atomic_cxchg_acqrel_failrelaxed(dst, old, new);
case .Sequentially_Consistent: return intrinsics.atomic_cxchg_failrelaxed(dst, old, new);
case: panic("an unknown ordering combination");
}
case Acquire:
case .Acquire:
switch success {
case Acquire: return intrinsics.atomic_cxchg_acq(dst, old, new);
case .Acquire: return intrinsics.atomic_cxchg_acq(dst, old, new);
case: panic("an unknown ordering combination");
}
case Sequentially_Consistent:
case .Sequentially_Consistent:
switch success {
case Sequentially_Consistent: return intrinsics.atomic_cxchg(dst, old, new);
case .Sequentially_Consistent: return intrinsics.atomic_cxchg(dst, old, new);
case: panic("an unknown ordering combination");
}
case Acquire_Release:
case .Acquire_Release:
panic("there is not such thing as an acquire/release failure ordering");
case Release:
case .Release:
panic("there is not such thing as an release failure ordering");
}
return T{}, false;
@@ -105,78 +99,72 @@ atomic_compare_exchange :: inline proc "contextless" (dst: ^$T, old, new: T, suc
atomic_add :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
using Ordering;
#complete switch order {
case Relaxed: return intrinsics.atomic_add_relaxed(dst, val);
case Release: return intrinsics.atomic_add_rel(dst, val);
case Acquire: return intrinsics.atomic_add_acq(dst, val);
case Acquire_Release: return intrinsics.atomic_add_acqrel(dst, val);
case Sequentially_Consistent: return intrinsics.atomic_add(dst, val);
case .Relaxed: return intrinsics.atomic_add_relaxed(dst, val);
case .Release: return intrinsics.atomic_add_rel(dst, val);
case .Acquire: return intrinsics.atomic_add_acq(dst, val);
case .Acquire_Release: return intrinsics.atomic_add_acqrel(dst, val);
case .Sequentially_Consistent: return intrinsics.atomic_add(dst, val);
}
panic("unknown order");
return T{};
}
atomic_sub :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
using Ordering;
#complete switch order {
case Relaxed: return intrinsics.atomic_sub_relaxed(dst, val);
case Release: return intrinsics.atomic_sub_rel(dst, val);
case Acquire: return intrinsics.atomic_sub_acq(dst, val);
case Acquire_Release: return intrinsics.atomic_sub_acqrel(dst, val);
case Sequentially_Consistent: return intrinsics.atomic_sub(dst, val);
case .Relaxed: return intrinsics.atomic_sub_relaxed(dst, val);
case .Release: return intrinsics.atomic_sub_rel(dst, val);
case .Acquire: return intrinsics.atomic_sub_acq(dst, val);
case .Acquire_Release: return intrinsics.atomic_sub_acqrel(dst, val);
case .Sequentially_Consistent: return intrinsics.atomic_sub(dst, val);
}
panic("unknown order");
return T{};
}
atomic_and :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
using Ordering;
#complete switch order {
case Relaxed: return intrinsics.atomic_and_relaxed(dst, val);
case Release: return intrinsics.atomic_and_rel(dst, val);
case Acquire: return intrinsics.atomic_and_acq(dst, val);
case Acquire_Release: return intrinsics.atomic_and_acqrel(dst, val);
case Sequentially_Consistent: return intrinsics.atomic_and(dst, val);
case .Relaxed: return intrinsics.atomic_and_relaxed(dst, val);
case .Release: return intrinsics.atomic_and_rel(dst, val);
case .Acquire: return intrinsics.atomic_and_acq(dst, val);
case .Acquire_Release: return intrinsics.atomic_and_acqrel(dst, val);
case .Sequentially_Consistent: return intrinsics.atomic_and(dst, val);
}
panic("unknown order");
return T{};
}
atomic_nand :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
using Ordering;
#complete switch order {
case Relaxed: return intrinsics.atomic_nand_relaxed(dst, val);
case Release: return intrinsics.atomic_nand_rel(dst, val);
case Acquire: return intrinsics.atomic_nand_acq(dst, val);
case Acquire_Release: return intrinsics.atomic_nand_acqrel(dst, val);
case Sequentially_Consistent: return intrinsics.atomic_nand(dst, val);
case .Relaxed: return intrinsics.atomic_nand_relaxed(dst, val);
case .Release: return intrinsics.atomic_nand_rel(dst, val);
case .Acquire: return intrinsics.atomic_nand_acq(dst, val);
case .Acquire_Release: return intrinsics.atomic_nand_acqrel(dst, val);
case .Sequentially_Consistent: return intrinsics.atomic_nand(dst, val);
}
panic("unknown order");
return T{};
}
atomic_or :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
using Ordering;
#complete switch order {
case Relaxed: return intrinsics.atomic_or_relaxed(dst, val);
case Release: return intrinsics.atomic_or_rel(dst, val);
case Acquire: return intrinsics.atomic_or_acq(dst, val);
case Acquire_Release: return intrinsics.atomic_or_acqrel(dst, val);
case Sequentially_Consistent: return intrinsics.atomic_or(dst, val);
case .Relaxed: return intrinsics.atomic_or_relaxed(dst, val);
case .Release: return intrinsics.atomic_or_rel(dst, val);
case .Acquire: return intrinsics.atomic_or_acq(dst, val);
case .Acquire_Release: return intrinsics.atomic_or_acqrel(dst, val);
case .Sequentially_Consistent: return intrinsics.atomic_or(dst, val);
}
panic("unknown order");
return T{};
}
atomic_xor :: inline proc "contextless" (dst: ^$T, val: T, order: Ordering) -> T {
using Ordering;
#complete switch order {
case Relaxed: return intrinsics.atomic_xor_relaxed(dst, val);
case Release: return intrinsics.atomic_xor_rel(dst, val);
case Acquire: return intrinsics.atomic_xor_acq(dst, val);
case Acquire_Release: return intrinsics.atomic_xor_acqrel(dst, val);
case Sequentially_Consistent: return intrinsics.atomic_xor(dst, val);
case .Relaxed: return intrinsics.atomic_xor_relaxed(dst, val);
case .Release: return intrinsics.atomic_xor_rel(dst, val);
case .Acquire: return intrinsics.atomic_xor_acq(dst, val);
case .Acquire_Release: return intrinsics.atomic_xor_acqrel(dst, val);
case .Sequentially_Consistent: return intrinsics.atomic_xor(dst, val);
}
panic("unknown order");
return T{};
+17 -7
View File
@@ -5,7 +5,6 @@ foreign import "system:kernel32.lib"
@(default_calling_convention = "std")
foreign kernel32 {
@(link_name="GetLastError") get_last_error :: proc() -> i32 ---;
@(link_name="CreateProcessA") create_process_a :: proc(application_name, command_line: cstring,
process_attributes, thread_attributes: ^Security_Attributes,
inherit_handle: Bool, creation_flags: u32, environment: rawptr,
@@ -18,8 +17,12 @@ foreign kernel32 {
process_information: ^Process_Information) -> Bool ---;
@(link_name="GetExitCodeProcess") get_exit_code_process :: proc(process: Handle, exit: ^u32) -> Bool ---;
@(link_name="ExitProcess") exit_process :: proc(exit_code: u32) ---;
@(link_name="GetModuleHandleA") get_module_handle_a :: proc(module_name: cstring) -> Hinstance ---;
@(link_name="GetModuleHandleW") get_module_handle_w :: proc(module_name: Wstring) -> Hinstance ---;
@(link_name="GetModuleHandleA") get_module_handle_a :: proc(module_name: cstring) -> Hmodule ---;
@(link_name="GetModuleHandleW") get_module_handle_w :: proc(module_name: Wstring) -> Hmodule ---;
@(link_name="GetModuleFileNameA") get_module_file_name_a :: proc(module: Hmodule, filename: cstring, size: u32) -> u32 ---;
@(link_name="GetModuleFileNameW") get_module_file_name_w :: proc(module: Hmodule, filename: Wstring, size: u32) -> u32 ---;
@(link_name="Sleep") sleep :: proc(ms: i32) -> i32 ---;
@(link_name="QueryPerformanceFrequency") query_performance_frequency :: proc(result: ^i64) -> i32 ---;
@(link_name="QueryPerformanceCounter") query_performance_counter :: proc(result: ^i64) -> i32 ---;
@@ -54,10 +57,6 @@ foreign kernel32 {
@(link_name="WriteFile") write_file :: proc(h: Handle, buf: rawptr, len: i32, written_result: ^i32, overlapped: rawptr) -> Bool ---;
@(link_name="GetFileSizeEx") get_file_size_ex :: proc(file_handle: Handle, file_size: ^i64) -> Bool ---;
@(link_name="GetFileAttributesA") get_file_attributes_a :: proc(filename: cstring) -> u32 ---;
@(link_name="GetFileAttributesW") get_file_attributes_w :: proc(filename: Wstring) -> u32 ---;
@(link_name="GetFileAttributesExA") get_file_attributes_ex_a :: proc(filename: cstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool ---;
@(link_name="GetFileAttributesExW") get_file_attributes_ex_w :: proc(filename: Wstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool ---;
@(link_name="GetFileInformationByHandle") get_file_information_by_handle :: proc(file_handle: Handle, file_info: ^By_Handle_File_Information) -> Bool ---;
@(link_name="CreateDirectoryA") create_directory_a :: proc(path: cstring, security_attributes: ^Security_Attributes) -> Bool ---;
@@ -117,6 +116,17 @@ foreign kernel32 {
@(link_name="WaitForSingleObject") wait_for_single_object :: proc(handle: Handle, milliseconds: u32) -> u32 ---;
}
@(default_calling_convention = "c")
foreign kernel32 {
@(link_name="GetLastError") get_last_error :: proc() -> i32 ---;
@(link_name="GetFileAttributesA") get_file_attributes_a :: proc(filename: cstring) -> u32 ---;
@(link_name="GetFileAttributesW") get_file_attributes_w :: proc(filename: Wstring) -> u32 ---;
@(link_name="GetFileAttributesExA") get_file_attributes_ex_a :: proc(filename: cstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool ---;
@(link_name="GetFileAttributesExW") get_file_attributes_ex_w :: proc(filename: Wstring, info_level_id: GET_FILEEX_INFO_LEVELS, file_info: ^File_Attribute_Data) -> Bool ---;
@(link_name="CompareFileTime") compare_file_time :: proc(a, b: ^Filetime) -> i32 ---;
}
@(default_calling_convention = "c")
foreign kernel32 {
@(link_name="InterlockedCompareExchange") interlocked_compare_exchange :: proc(dst: ^i32, exchange, comparand: i32) -> i32 ---;