mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-23 06:05:00 -07:00
Merge branch 'master' into master
This commit is contained in:
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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{};
|
||||
|
||||
@@ -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 ---;
|
||||
|
||||
@@ -92,6 +92,7 @@ struct BuildContext {
|
||||
|
||||
String out_filepath;
|
||||
String resource_filepath;
|
||||
String pdb_filepath;
|
||||
bool has_resource;
|
||||
String opt_flags;
|
||||
String llc_flags;
|
||||
|
||||
+12
-2
@@ -391,8 +391,18 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
|
||||
case Entity_Procedure:
|
||||
case Entity_LibraryName:
|
||||
case Entity_ImportName:
|
||||
override_entity_in_scope(e, entity);
|
||||
return;
|
||||
{
|
||||
override_entity_in_scope(e, entity);
|
||||
|
||||
DeclInfo *decl = decl_info_of_entity(e);
|
||||
if (decl != nullptr) {
|
||||
if (decl->attributes.count > 0) {
|
||||
error(decl->attributes[0], "Constant alias declarations cannot have attributes");
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -474,10 +474,11 @@ bool check_using_stmt_entity(CheckerContext *ctx, AstUsingStmt *us, Ast *expr, b
|
||||
case Entity_ImportName: {
|
||||
Scope *scope = e->ImportName.scope;
|
||||
for_array(i, scope->elements.entries) {
|
||||
String name = scope->elements.entries[i].key.string;
|
||||
Entity *decl = scope->elements.entries[i].value;
|
||||
if (!is_entity_exported(decl)) continue;
|
||||
|
||||
Entity *found = scope_insert(ctx->scope, decl);
|
||||
Entity *found = scope_insert_with_name(ctx->scope, name, decl);
|
||||
if (found != nullptr) {
|
||||
gbString expr_str = expr_to_string(expr);
|
||||
error(us->token,
|
||||
|
||||
@@ -2507,7 +2507,9 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
|
||||
case_ast_node(et, BitFieldType, e);
|
||||
*type = alloc_type_bit_field();
|
||||
set_base_type(named_type, *type);
|
||||
check_open_scope(ctx, e);
|
||||
check_bit_field_type(ctx, *type, e);
|
||||
check_close_scope(ctx);
|
||||
return true;
|
||||
case_end;
|
||||
|
||||
|
||||
+53
-35
@@ -388,8 +388,7 @@ Entity *scope_lookup(Scope *s, String name) {
|
||||
|
||||
|
||||
|
||||
Entity *scope_insert(Scope *s, Entity *entity) {
|
||||
String name = entity->token.string;
|
||||
Entity *scope_insert_with_name(Scope *s, String name, Entity *entity) {
|
||||
if (name == "") {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -406,6 +405,11 @@ Entity *scope_insert(Scope *s, Entity *entity) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Entity *scope_insert(Scope *s, Entity *entity) {
|
||||
String name = entity->token.string;
|
||||
return scope_insert_with_name(s, name, entity);
|
||||
}
|
||||
|
||||
|
||||
GB_COMPARE_PROC(entity_variable_pos_cmp) {
|
||||
Entity *x = *cast(Entity **)a;
|
||||
@@ -1023,39 +1027,41 @@ void add_entity_definition(CheckerInfo *i, Ast *identifier, Entity *entity) {
|
||||
array_add(&i->definitions, entity);
|
||||
}
|
||||
|
||||
bool add_entity(Checker *c, Scope *scope, Ast *identifier, Entity *entity) {
|
||||
bool redeclaration_error(String name, Entity *prev, Entity *found) {
|
||||
TokenPos pos = found->token.pos;
|
||||
Entity *up = found->using_parent;
|
||||
if (up != nullptr) {
|
||||
if (pos == up->token.pos) {
|
||||
// NOTE(bill): Error should have been handled already
|
||||
return false;
|
||||
}
|
||||
error(prev->token,
|
||||
"Redeclaration of '%.*s' in this scope through 'using'\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name),
|
||||
LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column);
|
||||
} else {
|
||||
if (pos == prev->token.pos) {
|
||||
// NOTE(bill): Error should have been handled already
|
||||
return false;
|
||||
}
|
||||
error(prev->token,
|
||||
"Redeclaration of '%.*s' in this scope\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name),
|
||||
LIT(pos.file), pos.line, pos.column);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool add_entity_with_name(Checker *c, Scope *scope, Ast *identifier, Entity *entity, String name) {
|
||||
if (scope == nullptr) {
|
||||
return false;
|
||||
}
|
||||
String name = entity->token.string;
|
||||
if (!is_blank_ident(name)) {
|
||||
Entity *ie = scope_insert(scope, entity);
|
||||
if (ie != nullptr) {
|
||||
TokenPos pos = ie->token.pos;
|
||||
Entity *up = ie->using_parent;
|
||||
if (up != nullptr) {
|
||||
if (pos == up->token.pos) {
|
||||
// NOTE(bill): Error should have been handled already
|
||||
return false;
|
||||
}
|
||||
error(entity->token,
|
||||
"Redeclaration of '%.*s' in this scope through 'using'\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name),
|
||||
LIT(up->token.pos.file), up->token.pos.line, up->token.pos.column);
|
||||
return false;
|
||||
} else {
|
||||
if (pos == entity->token.pos) {
|
||||
// NOTE(bill): Error should have been handled already
|
||||
return false;
|
||||
}
|
||||
error(entity->token,
|
||||
"Redeclaration of '%.*s' in this scope\n"
|
||||
"\tat %.*s(%td:%td)",
|
||||
LIT(name),
|
||||
LIT(pos.file), pos.line, pos.column);
|
||||
return false;
|
||||
}
|
||||
return redeclaration_error(name, entity, ie);
|
||||
}
|
||||
}
|
||||
if (identifier != nullptr) {
|
||||
@@ -1063,6 +1069,9 @@ bool add_entity(Checker *c, Scope *scope, Ast *identifier, Entity *entity) {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool add_entity(Checker *c, Scope *scope, Ast *identifier, Entity *entity) {
|
||||
return add_entity_with_name(c, scope, identifier, entity, entity->token.string);
|
||||
}
|
||||
|
||||
void add_entity_use(CheckerContext *c, Ast *identifier, Entity *entity) {
|
||||
if (entity == nullptr) {
|
||||
@@ -2005,7 +2014,7 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
||||
if (value != nullptr) {
|
||||
Operand o = {};
|
||||
check_expr(c, &o, value);
|
||||
Entity *e = entity_of_ident(o.expr);
|
||||
Entity *e = entity_of_node(o.expr);
|
||||
if (e != nullptr && e->kind == Entity_Procedure) {
|
||||
warning(elem, "'%.*s' is deprecated, please use one of the following instead: 'deferred_none', 'deferred_in', 'deferred_out'", LIT(name));
|
||||
if (ac->deferred_procedure.entity != nullptr) {
|
||||
@@ -2022,7 +2031,7 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
||||
if (value != nullptr) {
|
||||
Operand o = {};
|
||||
check_expr(c, &o, value);
|
||||
Entity *e = entity_of_ident(o.expr);
|
||||
Entity *e = entity_of_node(o.expr);
|
||||
if (e != nullptr && e->kind == Entity_Procedure) {
|
||||
ac->deferred_procedure.kind = DeferredProcedure_none;
|
||||
ac->deferred_procedure.entity = e;
|
||||
@@ -2035,7 +2044,7 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
||||
if (value != nullptr) {
|
||||
Operand o = {};
|
||||
check_expr(c, &o, value);
|
||||
Entity *e = entity_of_ident(o.expr);
|
||||
Entity *e = entity_of_node(o.expr);
|
||||
if (e != nullptr && e->kind == Entity_Procedure) {
|
||||
if (ac->deferred_procedure.entity != nullptr) {
|
||||
error(elem, "Previous usage of a 'deferred_*' attribute");
|
||||
@@ -2051,7 +2060,7 @@ DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
||||
if (value != nullptr) {
|
||||
Operand o = {};
|
||||
check_expr(c, &o, value);
|
||||
Entity *e = entity_of_ident(o.expr);
|
||||
Entity *e = entity_of_node(o.expr);
|
||||
if (e != nullptr && e->kind == Entity_Procedure) {
|
||||
if (ac->deferred_procedure.entity != nullptr) {
|
||||
error(elem, "Previous usage of a 'deferred_*' attribute");
|
||||
@@ -3050,12 +3059,21 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
|
||||
|
||||
// NOTE(bill): Add imported entities to this file's scope
|
||||
for_array(elem_index, scope->elements.entries) {
|
||||
String name = scope->elements.entries[elem_index].key.string;
|
||||
Entity *e = scope->elements.entries[elem_index].value;
|
||||
if (e->scope == parent_scope) continue;
|
||||
|
||||
if (is_entity_exported(e)) {
|
||||
Entity *prev = scope_lookup(parent_scope, e->token.string);
|
||||
add_entity(ctx->checker, parent_scope, e->identifier, e);
|
||||
Entity *found = scope_lookup_current(parent_scope, name);
|
||||
if (found != nullptr) {
|
||||
// NOTE(bill):
|
||||
// Date: 2019-03-17
|
||||
// The order has to be the other way around as `using` adds the entity into the that
|
||||
// file scope otherwise the error would be the wrong way around
|
||||
redeclaration_error(name, found, e);
|
||||
} else {
|
||||
add_entity_with_name(ctx->checker, parent_scope, e->identifier, e, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-5
@@ -2875,12 +2875,12 @@ irValue *ir_copy_value_to_ptr(irProcedure *proc, irValue *val, Type *new_type, i
|
||||
if (alignment < type_alignment) {
|
||||
alignment = type_alignment;
|
||||
}
|
||||
GB_ASSERT_MSG(are_types_identical(new_type, ir_type(val)), "%s %s", type_to_string(new_type), type_to_string(ir_type(val)));
|
||||
|
||||
irValue *ptr = ir_add_local_generated(proc, new_type, false);
|
||||
ptr->Instr.Local.alignment = alignment;
|
||||
ir_emit_store(proc, ptr, val);
|
||||
|
||||
if (val) val->uses += 1;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
@@ -2972,11 +2972,15 @@ irValue *ir_emit_call(irProcedure *p, irValue *value, Array<irValue *> args, Pro
|
||||
|
||||
Type *original_type = e->type;
|
||||
Type *new_type = pt->Proc.abi_compat_params[i];
|
||||
if (original_type != new_type) {
|
||||
if (is_type_pointer(new_type)) {
|
||||
Type *arg_type = ir_type(args[i]);
|
||||
if (are_types_identical(arg_type, new_type)) {
|
||||
// NOTE(bill): Done
|
||||
} else if (!are_types_identical(original_type, new_type)) {
|
||||
|
||||
if (is_type_pointer(new_type) && !is_type_pointer(original_type)) {
|
||||
if (e->flags&EntityFlag_Value) {
|
||||
args[i] = ir_address_from_load_or_generate_local(p, args[i]);
|
||||
} else {
|
||||
} else if (!is_type_pointer(arg_type)) {
|
||||
args[i] = ir_copy_value_to_ptr(p, args[i], original_type, 16);
|
||||
}
|
||||
} else if (is_type_integer(new_type)) {
|
||||
|
||||
+1
-1
@@ -471,7 +471,7 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t, bool in_struct) {
|
||||
i64 align_of_union = type_align_of(t);
|
||||
ir_write_byte(f, '{');
|
||||
ir_print_alignment_prefix_hack(f, align_of_union);
|
||||
ir_fprintf(f, ", [%lld x i8]}", align_of_union, size_of_union);
|
||||
ir_fprintf(f, ", [%lld x i8]}", size_of_union);
|
||||
return;
|
||||
} else {
|
||||
if (t->Struct.is_packed) {
|
||||
|
||||
+72
-35
@@ -18,7 +18,7 @@
|
||||
#include "ir_print.cpp"
|
||||
|
||||
// NOTE(bill): 'name' is used in debugging and profiling modes
|
||||
i32 system_exec_command_line_app(char *name, bool is_silent, char *fmt, ...) {
|
||||
i32 system_exec_command_line_app(char *name, char *fmt, ...) {
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
STARTUPINFOW start_info = {gb_size_of(STARTUPINFOW)};
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
@@ -201,7 +201,6 @@ enum BuildFlagKind {
|
||||
BuildFlag_Invalid,
|
||||
|
||||
BuildFlag_OutFile,
|
||||
BuildFlag_ResourceFile,
|
||||
BuildFlag_OptimizationLevel,
|
||||
BuildFlag_ShowTimings,
|
||||
BuildFlag_ThreadCount,
|
||||
@@ -218,6 +217,11 @@ enum BuildFlagKind {
|
||||
BuildFlag_Vet,
|
||||
BuildFlag_IgnoreUnknownAttributes,
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
BuildFlag_ResourceFile,
|
||||
BuildFlag_WindowsPdbName,
|
||||
#endif
|
||||
|
||||
BuildFlag_COUNT,
|
||||
};
|
||||
|
||||
@@ -281,7 +285,6 @@ ExactValue build_param_to_exact_value(String name, String param) {
|
||||
bool parse_build_flags(Array<String> args) {
|
||||
auto build_flags = array_make<BuildFlag>(heap_allocator(), 0, BuildFlag_COUNT);
|
||||
add_flag(&build_flags, BuildFlag_OutFile, str_lit("out"), BuildFlagParam_String);
|
||||
add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String);
|
||||
add_flag(&build_flags, BuildFlag_OptimizationLevel, str_lit("opt"), BuildFlagParam_Integer);
|
||||
add_flag(&build_flags, BuildFlag_ShowTimings, str_lit("show-timings"), BuildFlagParam_None);
|
||||
add_flag(&build_flags, BuildFlag_ThreadCount, str_lit("thread-count"), BuildFlagParam_Integer);
|
||||
@@ -296,7 +299,12 @@ bool parse_build_flags(Array<String> args) {
|
||||
add_flag(&build_flags, BuildFlag_NoCRT, str_lit("no-crt"), BuildFlagParam_None);
|
||||
add_flag(&build_flags, BuildFlag_UseLLD, str_lit("lld"), BuildFlagParam_None);
|
||||
add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None);
|
||||
add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("-ignore-unknown-attributes"), BuildFlagParam_None);
|
||||
add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None);
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String);
|
||||
add_flag(&build_flags, BuildFlag_WindowsPdbName, str_lit("pdb-name"), BuildFlagParam_String);
|
||||
#endif
|
||||
|
||||
GB_ASSERT(args.count >= 3);
|
||||
Array<String> flag_args = array_slice(args, 3, args.count);
|
||||
@@ -436,24 +444,6 @@ bool parse_build_flags(Array<String> args) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BuildFlag_ResourceFile: {
|
||||
GB_ASSERT(value.kind == ExactValue_String);
|
||||
String path = value.value_string;
|
||||
path = string_trim_whitespace(path);
|
||||
if (is_import_path_valid(path)) {
|
||||
if(!string_ends_with(path, str_lit(".rc"))) {
|
||||
gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
|
||||
bad_flags = true;
|
||||
break;
|
||||
}
|
||||
build_context.resource_filepath = substring(path, 0, string_extension_position(path));
|
||||
build_context.has_resource = true;
|
||||
} else {
|
||||
gb_printf_err("Invalid -resource path, got %.*s\n", LIT(path));
|
||||
bad_flags = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BuildFlag_OptimizationLevel:
|
||||
GB_ASSERT(value.kind == ExactValue_Integer);
|
||||
build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer);
|
||||
@@ -671,6 +661,48 @@ bool parse_build_flags(Array<String> args) {
|
||||
case BuildFlag_IgnoreUnknownAttributes:
|
||||
build_context.ignore_unknown_attributes = true;
|
||||
break;
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
case BuildFlag_ResourceFile: {
|
||||
GB_ASSERT(value.kind == ExactValue_String);
|
||||
String path = value.value_string;
|
||||
path = string_trim_whitespace(path);
|
||||
if (is_import_path_valid(path)) {
|
||||
if(!string_ends_with(path, str_lit(".rc"))) {
|
||||
gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
|
||||
bad_flags = true;
|
||||
break;
|
||||
}
|
||||
build_context.resource_filepath = substring(path, 0, string_extension_position(path));
|
||||
build_context.has_resource = true;
|
||||
} else {
|
||||
gb_printf_err("Invalid -resource path, got %.*s\n", LIT(path));
|
||||
bad_flags = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BuildFlag_WindowsPdbName: {
|
||||
GB_ASSERT(value.kind == ExactValue_String);
|
||||
String path = value.value_string;
|
||||
path = string_trim_whitespace(path);
|
||||
if (is_import_path_valid(path)) {
|
||||
// #if defined(GB_SYSTEM_WINDOWS)
|
||||
// String ext = path_extension(path);
|
||||
// if (ext != ".pdb") {
|
||||
// path = substring(path, 0, string_extension_position(path));
|
||||
// }
|
||||
// #endif
|
||||
build_context.pdb_filepath = path;
|
||||
} else {
|
||||
gb_printf_err("Invalid -pdb-name path, got %.*s\n", LIT(path));
|
||||
bad_flags = true;
|
||||
}
|
||||
break;
|
||||
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -784,7 +816,7 @@ void remove_temp_files(String output_base) {
|
||||
i32 exec_llvm_opt(String output_base) {
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
// For more passes arguments: http://llvm.org/docs/Passes.html
|
||||
return system_exec_command_line_app("llvm-opt", false,
|
||||
return system_exec_command_line_app("llvm-opt",
|
||||
"\"%.*sbin/opt\" \"%.*s.ll\" -o \"%.*s.bc\" %.*s "
|
||||
"",
|
||||
LIT(build_context.ODIN_ROOT),
|
||||
@@ -793,7 +825,7 @@ i32 exec_llvm_opt(String output_base) {
|
||||
#else
|
||||
// NOTE(zangent): This is separate because it seems that LLVM tools are packaged
|
||||
// with the Windows version, while they will be system-provided on MacOS and GNU/Linux
|
||||
return system_exec_command_line_app("llvm-opt", false,
|
||||
return system_exec_command_line_app("llvm-opt",
|
||||
"opt \"%.*s.ll\" -o \"%.*s.bc\" %.*s "
|
||||
"",
|
||||
LIT(output_base), LIT(output_base),
|
||||
@@ -804,10 +836,10 @@ i32 exec_llvm_opt(String output_base) {
|
||||
i32 exec_llvm_llc(String output_base) {
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
|
||||
return system_exec_command_line_app("llvm-llc", false,
|
||||
return system_exec_command_line_app("llvm-llc",
|
||||
"\"%.*sbin\\llc\" \"%.*s.bc\" -filetype=obj -O%d "
|
||||
"-o \"%.*s.obj\" "
|
||||
"%.*s "
|
||||
"%.*s"
|
||||
"",
|
||||
LIT(build_context.ODIN_ROOT),
|
||||
LIT(output_base),
|
||||
@@ -817,7 +849,7 @@ i32 exec_llvm_llc(String output_base) {
|
||||
#else
|
||||
// NOTE(zangent): Linux / Unix is unfinished and not tested very well.
|
||||
// For more arguments: http://llvm.org/docs/CommandGuide/llc.html
|
||||
return system_exec_command_line_app("llc", false,
|
||||
return system_exec_command_line_app("llc",
|
||||
"llc \"%.*s.bc\" -filetype=obj -relocation-model=pic -O%d "
|
||||
"%.*s "
|
||||
"%s"
|
||||
@@ -1041,6 +1073,11 @@ int main(int arg_count, char **arg_ptr) {
|
||||
} else {
|
||||
link_settings = gb_string_append_fmt(link_settings, "/ENTRY:mainCRTStartup");
|
||||
}
|
||||
|
||||
if (build_context.pdb_filepath != "") {
|
||||
link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(build_context.pdb_filepath));
|
||||
}
|
||||
|
||||
if (build_context.no_crt) {
|
||||
link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
|
||||
} else {
|
||||
@@ -1052,7 +1089,7 @@ int main(int arg_count, char **arg_ptr) {
|
||||
}
|
||||
if (!build_context.use_lld) { // msvc
|
||||
if (build_context.has_resource) {
|
||||
exit_code = system_exec_command_line_app("msvc-link", true,
|
||||
exit_code = system_exec_command_line_app("msvc-link",
|
||||
"rc /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
|
||||
LIT(output_base),
|
||||
LIT(build_context.resource_filepath)
|
||||
@@ -1062,7 +1099,7 @@ int main(int arg_count, char **arg_ptr) {
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
exit_code = system_exec_command_line_app("msvc-link", true,
|
||||
exit_code = system_exec_command_line_app("msvc-link",
|
||||
"link \"%.*s.obj\" \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
|
||||
" %.*s "
|
||||
@@ -1073,7 +1110,7 @@ int main(int arg_count, char **arg_ptr) {
|
||||
link_settings
|
||||
);
|
||||
} else {
|
||||
exit_code = system_exec_command_line_app("msvc-link", true,
|
||||
exit_code = system_exec_command_line_app("msvc-link",
|
||||
"link \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
|
||||
" %.*s "
|
||||
@@ -1085,7 +1122,7 @@ int main(int arg_count, char **arg_ptr) {
|
||||
);
|
||||
}
|
||||
} else { // lld
|
||||
exit_code = system_exec_command_line_app("msvc-link", true,
|
||||
exit_code = system_exec_command_line_app("msvc-link",
|
||||
"\"%.*s\\bin\\lld-link\" \"%.*s.obj\" -OUT:\"%.*s.%s\" %s "
|
||||
"/nologo /incremental:no /opt:ref /subsystem:CONSOLE "
|
||||
" %.*s "
|
||||
@@ -1109,7 +1146,7 @@ int main(int arg_count, char **arg_ptr) {
|
||||
remove_temp_files(output_base);
|
||||
|
||||
if (run_output) {
|
||||
system_exec_command_line_app("odin run", false, "%.*s.exe %.*s", LIT(output_base), LIT(run_args_string));
|
||||
system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(output_base), LIT(run_args_string));
|
||||
}
|
||||
#else
|
||||
timings_start_section(&timings, str_lit("ld-link"));
|
||||
@@ -1206,7 +1243,7 @@ int main(int arg_count, char **arg_ptr) {
|
||||
}
|
||||
#endif
|
||||
|
||||
exit_code = system_exec_command_line_app("ld-link", true,
|
||||
exit_code = system_exec_command_line_app("ld-link",
|
||||
"%s \"%.*s.o\" -o \"%.*s%.*s\" %s "
|
||||
" %s "
|
||||
" %.*s "
|
||||
@@ -1235,7 +1272,7 @@ int main(int arg_count, char **arg_ptr) {
|
||||
if (build_context.ODIN_DEBUG) {
|
||||
// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
|
||||
// to the symbols in the object file
|
||||
exit_code = system_exec_command_line_app("dsymutil", true,
|
||||
exit_code = system_exec_command_line_app("dsymutil",
|
||||
"dsymutil %.*s%.*s", LIT(output_base), LIT(output_ext)
|
||||
);
|
||||
|
||||
@@ -1256,7 +1293,7 @@ int main(int arg_count, char **arg_ptr) {
|
||||
//NOTE(thebirk): This whole thing is a little leaky
|
||||
String complete_path = concatenate_strings(heap_allocator(), output_base, output_ext);
|
||||
complete_path = path_to_full_path(heap_allocator(), complete_path);
|
||||
system_exec_command_line_app("odin run", false, "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
|
||||
system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+5
-3
@@ -212,6 +212,8 @@ void warning_va(Token token, char *fmt, va_list va) {
|
||||
gb_mutex_unlock(&global_error_collector.mutex);
|
||||
}
|
||||
|
||||
#define MAX_ERROR_COLLECTOR_COUNT (36)
|
||||
|
||||
void error_va(Token token, char *fmt, va_list va) {
|
||||
gb_mutex_lock(&global_error_collector.mutex);
|
||||
global_error_collector.count++;
|
||||
@@ -225,7 +227,7 @@ void error_va(Token token, char *fmt, va_list va) {
|
||||
gb_bprintf_va(fmt, va));
|
||||
}
|
||||
gb_mutex_unlock(&global_error_collector.mutex);
|
||||
if (global_error_collector.count > 20) {
|
||||
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
|
||||
gb_exit(1);
|
||||
}
|
||||
}
|
||||
@@ -243,7 +245,7 @@ void error_no_newline_va(Token token, char *fmt, va_list va) {
|
||||
gb_bprintf_va(fmt, va));
|
||||
}
|
||||
gb_mutex_unlock(&global_error_collector.mutex);
|
||||
if (global_error_collector.count > 20) {
|
||||
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
|
||||
gb_exit(1);
|
||||
}
|
||||
}
|
||||
@@ -263,7 +265,7 @@ void syntax_error_va(Token token, char *fmt, va_list va) {
|
||||
}
|
||||
|
||||
gb_mutex_unlock(&global_error_collector.mutex);
|
||||
if (global_error_collector.count > 20) {
|
||||
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
|
||||
gb_exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user