Files
Odin/core/fmt.odin
Ginger Bill 77e219d442 Change var decl syntax
`var x int;` from `x: int;`
2016-12-18 22:32:18 +00:00

595 lines
12 KiB
Odin

#import "os.odin";
#import "mem.odin";
#import "utf8.odin";
const PRINT_BUF_SIZE = 1<<12;
proc fprint(f ^os.File, args ..any) -> int {
var data [PRINT_BUF_SIZE]byte;
var buf = data[:0];
bprint(^buf, ..args);
os.write(f, buf);
return buf.count;
}
proc fprintln(f ^os.File, args ..any) -> int {
var data [PRINT_BUF_SIZE]byte;
var buf = data[:0];
bprintln(^buf, ..args);
os.write(f, buf);
return buf.count;
}
proc fprintf(f ^os.File, fmt string, args ..any) -> int {
var data [PRINT_BUF_SIZE]byte;
var buf = data[:0];
bprintf(^buf, fmt, ..args);
os.write(f, buf);
return buf.count;
}
proc print(args ..any) -> int {
return fprint(os.stdout, ..args);
}
proc println(args ..any) -> int {
return fprintln(os.stdout, ..args);
}
proc printf(fmt string, args ..any) -> int {
return fprintf(os.stdout, fmt, ..args);
}
proc fprint_type(f ^os.File, info ^Type_Info) {
var data [PRINT_BUF_SIZE]byte;
var buf = data[:0];
bprint_type(^buf, info);
os.write(f, buf);
}
proc print_byte_buffer(buf ^[]byte, b []byte) {
if buf.count < buf.capacity {
var n = min(buf.capacity-buf.count, b.count);
if n > 0 {
mem.copy(buf.data + buf.count, b.data, n);
buf.count += n;
}
}
}
proc bprint_string(buf ^[]byte, s string) {
print_byte_buffer(buf, s as []byte);
}
proc byte_reverse(b []byte) {
var n = b.count;
for var i = 0; i < n/2; i++ {
b[i], b[n-1-i] = b[n-1-i], b[i];
}
}
proc bprint_rune(buf ^[]byte, r rune) {
var b, n = utf8.encode_rune(r);
bprint_string(buf, b[:n] as string);
}
proc bprint_space(buf ^[]byte) { bprint_rune(buf, ' '); }
proc bprint_nl (buf ^[]byte) { bprint_rune(buf, '\n'); }
var __NUM_TO_CHAR_TABLE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz@$";
proc bprint_bool(buffer ^[]byte, b bool) {
if b {
bprint_string(buffer, "true");
} else {
bprint_string(buffer, "false");
}
}
proc bprint_pointer(buffer ^[]byte, p rawptr) #inline {
bprint_string(buffer, "0x");
bprint_u64(buffer, p as uint as u64);
}
proc bprint_f16 (buffer ^[]byte, f f32) #inline { print__f64(buffer, f as f64, 4); }
proc bprint_f32 (buffer ^[]byte, f f32) #inline { print__f64(buffer, f as f64, 7); }
proc bprint_f64 (buffer ^[]byte, f f64) #inline { print__f64(buffer, f as f64, 16); }
proc bprint_u64(buffer ^[]byte, value u64) {
var i = value;
var buf [20]byte;
var len = 0;
if i == 0 {
buf[len] = '0';
len++;
}
for i > 0 {
buf[len] = __NUM_TO_CHAR_TABLE[i % 10];
len++;
i /= 10;
}
byte_reverse(buf[:len]);
bprint_string(buffer, buf[:len] as string);
}
proc bprint_i64(buffer ^[]byte, value i64) {
// TODO(bill): Cleanup printing
var i = value;
if i < 0 {
i = -i;
bprint_rune(buffer, '-');
}
bprint_u64(buffer, i as u64);
}
/*
proc bprint_u128(buffer ^[]byte, value u128) {
var a = value transmute [2]u64;
if a[1] != 0 {
bprint_u64(buffer, a[1]);
}
bprint_u64(buffer, a[0]);
}
proc bprint_i128(buffer ^[]byte, value i128) {
var i = value;
if i < 0 {
i = -i;
bprint_rune(buffer, '-');
}
bprint_u128(buffer, i as u128);
}
*/
proc print__f64(buffer ^[]byte, value f64, decimal_places int) {
var f = value;
if f == 0 {
bprint_rune(buffer, '0');
return;
}
if f < 0 {
bprint_rune(buffer, '-');
f = -f;
}
var i = f as u64;
bprint_u64(buffer, i);
f -= i as f64;
bprint_rune(buffer, '.');
var mult f64 = 10.0;
for ; decimal_places >= 0; decimal_places-- {
i = (f * mult) as u64;
bprint_u64(buffer, i as u64);
f -= i as f64 / mult;
mult *= 10;
}
}
proc bprint_type(buf ^[]byte, ti ^Type_Info) {
if ti == nil {
return;
}
using Type_Info;
match type info : ti {
case Named:
bprint_string(buf, info.name);
case Integer:
match {
case ti == type_info(int): bprint_string(buf, "int");
case ti == type_info(uint): bprint_string(buf, "uint");
default:
if info.signed {
bprint_string(buf, "i");
} else {
bprint_string(buf, "u");
}
bprint_u64(buf, 8*info.size as u64);
}
case Float:
match info.size {
case 4: bprint_string(buf, "f32");
case 8: bprint_string(buf, "f64");
}
case String: bprint_string(buf, "string");
case Boolean: bprint_string(buf, "bool");
case Pointer:
if info.elem == nil {
bprint_string(buf, "rawptr");
} else {
bprint_string(buf, "^");
bprint_type(buf, info.elem);
}
case Maybe:
bprint_string(buf, "?");
bprint_type(buf, info.elem);
case Procedure:
bprint_string(buf, "proc");
if info.params == nil {
bprint_string(buf, "()");
} else {
var count = (info.params as ^Tuple).fields.count;
if count == 1 { bprint_string(buf, "("); }
bprint_type(buf, info.params);
if count == 1 { bprint_string(buf, ")"); }
}
if info.results != nil {
bprint_string(buf, " -> ");
bprint_type(buf, info.results);
}
case Tuple:
var count = info.fields.count;
if count != 1 { bprint_string(buf, "("); }
for var i = 0; i < count; i++ {
if i > 0 { bprint_string(buf, ", "); }
var f = info.fields[i];
if f.name.count > 0 {
bprint_string(buf, f.name);
bprint_string(buf, ": ");
}
bprint_type(buf, f.type_info);
}
if count != 1 { bprint_string(buf, ")"); }
case Array:
bprint_string(buf, "[");
bprint_i64(buf, info.count as i64);
bprint_string(buf, "]");
bprint_type(buf, info.elem);
case Slice:
bprint_string(buf, "[");
bprint_string(buf, "]");
bprint_type(buf, info.elem);
case Vector:
bprint_string(buf, "[vector ");
bprint_i64(buf, info.count as i64);
bprint_string(buf, "]");
bprint_type(buf, info.elem);
case Struct:
bprint_string(buf, "struct ");
if info.packed { bprint_string(buf, "#packed "); }
if info.ordered { bprint_string(buf, "#ordered "); }
bprint_string(buf, "{");
for var i = 0; i < info.fields.count; i++ {
if i > 0 {
bprint_string(buf, ", ");
}
bprint_any(buf, info.fields[i].name);
bprint_string(buf, ": ");
bprint_type(buf, info.fields[i].type_info);
}
bprint_string(buf, "}");
case Union:
bprint_string(buf, "union {");
for var i = 0; i < info.fields.count; i++ {
if i > 0 {
bprint_string(buf, ", ");
}
bprint_any(buf, info.fields[i].name);
bprint_string(buf, ": ");
bprint_type(buf, info.fields[i].type_info);
}
bprint_string(buf, "}");
case Raw_Union:
bprint_string(buf, "raw_union {");
for var i = 0; i < info.fields.count; i++ {
if i > 0 {
bprint_string(buf, ", ");
}
bprint_any(buf, info.fields[i].name);
bprint_string(buf, ": ");
bprint_type(buf, info.fields[i].type_info);
}
bprint_string(buf, "}");
case Enum:
bprint_string(buf, "enum ");
bprint_type(buf, info.base);
bprint_string(buf, "{}");
}
}
proc make_any(type_info ^Type_Info, data rawptr) -> any {
var a any;
a.type_info = type_info;
a.data = data;
return a;
}
proc bprint_any(buf ^[]byte, arg any) {
if arg.type_info == nil {
bprint_string(buf, "<nil>");
return;
}
if arg.data == nil {
bprint_string(buf, "<nil>");
return;
}
using Type_Info;
match type info : arg.type_info {
case Named:
var a = make_any(info.base, arg.data);
match type b : info.base {
case Struct:
bprint_string(buf, info.name);
bprint_string(buf, "{");
for var i = 0; i < b.fields.count; i++ {
var f = b.fields[i];
if i > 0 {
bprint_string(buf, ", ");
}
bprint_string(buf, f.name);
// bprint_any(buf, f.offset);
bprint_string(buf, " = ");
var data = arg.data as ^byte + f.offset;
bprint_any(buf, make_any(f.type_info, data));
}
bprint_string(buf, "}");
default:
bprint_any(buf, a);
}
case Integer:
match type i : arg {
case i8: bprint_i64(buf, i as i64);
case u8: bprint_u64(buf, i as u64);
case i16: bprint_i64(buf, i as i64);
case u16: bprint_u64(buf, i as u64);
case i32: bprint_i64(buf, i as i64);
case u32: bprint_u64(buf, i as u64);
case i64: bprint_i64(buf, i);
case u64: bprint_u64(buf, i);
// case i128: bprint_i128(buf, i);
// case u128: bprint_u128(buf, i);
case int: bprint_i64(buf, i as i64);
case uint: bprint_u64(buf, i as u64);
}
case Float:
match type f : arg {
// case f16: bprint_f64(buf, f as f64);
case f32: bprint_f32(buf, f);
case f64: bprint_f64(buf, f);
// case f128: bprint_f64(buf, f as f64);
}
case String:
match type s : arg {
case string: bprint_string(buf, s);
}
case Boolean:
match type b : arg {
case bool: bprint_bool(buf, b);
}
case Pointer:
match type p : arg {
case ^Type_Info: bprint_type(buf, p);
default: bprint_pointer(buf, (arg.data as ^rawptr)^);
}
case Maybe:
var size = mem.size_of_type_info(info.elem);
var data = slice_ptr(arg.data as ^byte, size+1);
if data[size] != 0 {
bprint_any(buf, make_any(info.elem, arg.data));
} else {
bprint_string(buf, "nil");
}
case Enum:
var value i64 = 0;
match type i : make_any(info.base, arg.data) {
case i8: value = i as i64;
case i16: value = i as i64;
case i32: value = i as i64;
case i64: value = i as i64;
case u8: value = i as i64;
case u16: value = i as i64;
case u32: value = i as i64;
case u64: value = i as i64;
}
bprint_string(buf, __enum_to_string(arg.type_info, value));
case Array:
bprintf(buf, "[%]%{", info.count, info.elem);
defer bprint_string(buf, "}");
for var i = 0; i < info.count; i++ {
if i > 0 {
bprint_string(buf, ", ");
}
var data = arg.data as ^byte + i*info.elem_size;
bprint_any(buf, make_any(info.elem, data));
}
case Slice:
var slice = arg.data as ^[]byte;
bprintf(buf, "[]%{", info.elem);
defer bprint_string(buf, "}");
for var i = 0; i < slice.count; i++ {
if i > 0 {
bprint_string(buf, ", ");
}
var data = slice.data + i*info.elem_size;
bprint_any(buf, make_any(info.elem, data));
}
case Vector:
proc is_bool(type_info ^Type_Info) -> bool {
match type info : type_info {
case Named:
return is_bool(info.base);
case Boolean:
return true;
}
return false;
}
bprintf(buf, "[vector %]%{", info.count, info.elem);
defer bprint_string(buf, "}");
if is_bool(info.elem) {
return;
}
for var i = 0; i < info.count; i++ {
if i > 0 {
bprint_string(buf, ", ");
}
var data = arg.data as ^byte + i*info.elem_size;
bprint_any(buf, make_any(info.elem, data));
}
case Struct:
bprintf(buf, "%{", arg.type_info);
defer bprint_string(buf, "}");
for var i = 0; i < info.fields.count; i++ {
if i > 0 {
bprint_string(buf, ", ");
}
bprint_string(buf, info.fields[i].name);
bprint_string(buf, " = ");
var data = arg.data as ^byte + info.fields[i].offset;
var ti = info.fields[i].type_info;
bprint_any(buf, make_any(ti, data));
}
case Union:
bprint_string(buf, "(union)");
case Raw_Union:
bprint_string(buf, "(raw_union)");
case Procedure:
bprint_type(buf, arg.type_info);
bprint_string(buf, " @ ");
bprint_pointer(buf, (arg.data as ^rawptr)^);
}
}
proc bprintf(buf ^[]byte, fmt string, args ..any) -> int {
proc is_digit(r rune) -> bool #inline {
return '0' <= r && r <= '9';
}
proc parse_int(s string, offset int) -> (int, int) {
var result = 0;
for ; offset < s.count; offset++ {
var c = s[offset] as rune;
if !is_digit(c) {
break;
}
result *= 10;
result += (c - '0') as int;
}
return result, offset;
}
var prev = 0;
var implicit_index = 0;
for var i = 0; i < fmt.count; i++ {
var r = fmt[i] as rune;
var index = implicit_index;
if r != '%' {
continue;
}
bprint_string(buf, fmt[prev:i]);
i++; // Skip %
if i < fmt.count {
var next = fmt[i] as rune;
if next == '%' {
bprint_string(buf, "%");
i++;
prev = i;
continue;
}
if is_digit(next) {
index, i = parse_int(fmt, i);
}
}
if 0 <= index && index < args.count {
bprint_any(buf, args[index]);
implicit_index = index+1;
} else {
// TODO(bill): Error check index out bounds
bprint_string(buf, "<invalid>");
}
prev = i;
}
bprint_string(buf, fmt[prev:]);
return buf.count;
}
proc bprint(buf ^[]byte, args ..any) -> int {
proc is_type_string(info ^Type_Info) -> bool {
using Type_Info;
if info == nil {
return false;
}
match type i : type_info_base(info) {
case String:
return true;
}
return false;
}
var prev_string = false;
for var i = 0; i < args.count; i++ {
var arg = args[i];
var is_string = arg.data != nil && is_type_string(arg.type_info);
if i > 0 && !is_string && !prev_string {
bprint_space(buf);
}
bprint_any(buf, arg);
prev_string = is_string;
}
return buf.count;
}
proc bprintln(buf ^[]byte, args ..any) -> int {
for var i = 0; i < args.count; i++ {
if i > 0 {
append(buf, ' ');
}
bprint_any(buf, args[i]);
}
bprint_nl(buf);
return buf.count;
}