mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 20:11:17 +00:00
444 lines
9.6 KiB
C
444 lines
9.6 KiB
C
module std::io;
|
|
import std::collections::map;
|
|
import libc;
|
|
|
|
const int PRINTF_NTOA_BUFFER_SIZE = 256;
|
|
|
|
fault PrintFault
|
|
{
|
|
BUFFER_EXCEEDED,
|
|
INTERNAL_BUFFER_EXCEEDED,
|
|
INVALID_FORMAT_STRING,
|
|
MISSING_ARG,
|
|
INVALID_ARGUMENT_TYPE,
|
|
}
|
|
|
|
fault FormattingFault
|
|
{
|
|
UNTERMINATED_FORMAT,
|
|
MISSING_ARG,
|
|
INVALID_WIDTH_ARG,
|
|
INVALID_FORMAT_TYPE,
|
|
}
|
|
|
|
def OutputFn = fn void!(char c, void* buffer);
|
|
def FloatType = double;
|
|
|
|
fn String any.to_string(void* value, Allocator *using) @interface;
|
|
fn void! any.to_format(void* value, Formatter* formatter) @interface;
|
|
|
|
fn usz! printf(String format, args...) @maydiscard
|
|
{
|
|
Formatter formatter;
|
|
formatter.init(&out_putchar_fn);
|
|
return formatter.vprintf(format, args);
|
|
}
|
|
|
|
fn usz! printfn(String format, args...) @maydiscard
|
|
{
|
|
Formatter formatter;
|
|
formatter.init(&out_putchar_fn);
|
|
usz len = formatter.vprintf(format, args)!;
|
|
putchar('\n');
|
|
return len + 1;
|
|
}
|
|
|
|
fn char[]! bprintf(char[] buffer, String format, args...) @maydiscard
|
|
{
|
|
Formatter formatter;
|
|
BufferData data = { .buffer = buffer };
|
|
formatter.init(&out_buffer_fn, &data);
|
|
usz size = formatter.vprintf(format, args)!;
|
|
return buffer[:data.written];
|
|
}
|
|
|
|
fn usz! File.printf(self, String format, args...) @maydiscard
|
|
{
|
|
Formatter formatter;
|
|
formatter.init(&out_fputchar_fn, &self);
|
|
return formatter.vprintf(format, args)!;
|
|
}
|
|
|
|
fn usz! File.printfn(self, String format, args...) @maydiscard
|
|
{
|
|
Formatter formatter;
|
|
formatter.init(&out_fputchar_fn, &self);
|
|
usz len = formatter.vprintf(format, args)!;
|
|
self.putc('\n')!;
|
|
self.flush();
|
|
return len + 1;
|
|
}
|
|
|
|
fn usz! Formatter.printf(&self, String format, args...)
|
|
{
|
|
return self.vprintf(format, args) @inline;
|
|
}
|
|
|
|
struct Formatter
|
|
{
|
|
void *data;
|
|
OutputFn out_fn;
|
|
struct
|
|
{
|
|
PrintFlags flags;
|
|
uint width;
|
|
uint prec;
|
|
usz idx;
|
|
}
|
|
}
|
|
|
|
bitstruct PrintFlags : uint
|
|
{
|
|
bool zeropad : 0;
|
|
bool left : 1;
|
|
bool plus : 2;
|
|
bool space : 3;
|
|
bool hash : 4;
|
|
bool uppercase : 5;
|
|
bool precision : 6;
|
|
}
|
|
|
|
fn void Formatter.init(&self, OutputFn out_fn, void* data = null)
|
|
{
|
|
*self = { .data = data, .out_fn = out_fn};
|
|
}
|
|
|
|
fn void! Formatter.out(&self, char c) @private
|
|
{
|
|
self.out_fn(c, self.data)!;
|
|
}
|
|
|
|
macro bool! Formatter.print_with_function(&self, any arg)
|
|
{
|
|
if (&arg.to_format)
|
|
{
|
|
PrintFlags old = self.flags;
|
|
uint old_width = self.width;
|
|
uint old_prec = self.prec;
|
|
defer
|
|
{
|
|
self.flags = old;
|
|
self.width = old_width;
|
|
self.prec = old_prec;
|
|
}
|
|
arg.to_format(self)!;
|
|
return true;
|
|
}
|
|
if (&arg.to_string)
|
|
{
|
|
PrintFlags old = self.flags;
|
|
uint old_width = self.width;
|
|
uint old_prec = self.prec;
|
|
defer
|
|
{
|
|
self.flags = old;
|
|
self.width = old_width;
|
|
self.prec = old_prec;
|
|
}
|
|
@stack_mem(512; Allocator* mem)
|
|
{
|
|
self.out_substr(arg.to_string(mem))!;
|
|
return true;
|
|
};
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
fn void! Formatter.out_str(&self, any arg) @private
|
|
{
|
|
switch (arg.type.kindof)
|
|
{
|
|
case TYPEID:
|
|
return self.out_substr("typeid");
|
|
case VOID:
|
|
return self.out_substr("void");
|
|
case ANYFAULT:
|
|
case FAULT:
|
|
return self.out_substr((*(anyfault*)arg.ptr).nameof);
|
|
case ANY:
|
|
return self.out_str(*(any*)arg);
|
|
case ENUM:
|
|
if (self.print_with_function(arg)!) return;
|
|
return self.out_substr(arg.type.names[types::any_to_int(arg, usz)!!]);
|
|
case STRUCT:
|
|
if (self.print_with_function(arg)!) return;
|
|
return self.out_substr("<struct>");
|
|
case UNION:
|
|
if (self.print_with_function(arg)!) return;
|
|
return self.out_substr("<union>");
|
|
case BITSTRUCT:
|
|
if (self.print_with_function(arg)!) return;
|
|
return self.out_substr("<bitstruct>");
|
|
case FUNC:
|
|
if (self.print_with_function(arg)!) return;
|
|
return self.out_substr("<function>");
|
|
case OPTIONAL:
|
|
unreachable();
|
|
case DISTINCT:
|
|
if (self.print_with_function(arg)!) return;
|
|
if (arg.type == DString.typeid)
|
|
{
|
|
return self.out_substr(((DString*)arg).as_str());
|
|
}
|
|
return self.out_str(any { arg.ptr, arg.type.inner });
|
|
case POINTER:
|
|
if (self.print_with_function(arg)!) return;
|
|
return self.ntoa_any(arg, 16);
|
|
case SIGNED_INT:
|
|
case UNSIGNED_INT:
|
|
return self.ntoa_any(arg, 10);
|
|
case FLOAT:
|
|
return self.ftoa(float_from_any(arg)!!);
|
|
case ARRAY:
|
|
if (self.print_with_function(arg)!) return;
|
|
// this is SomeType[*] so grab the "SomeType"
|
|
typeid inner = arg.type.inner;
|
|
usz size = inner.sizeof;
|
|
usz len = arg.type.len;
|
|
// Pretend this is a String
|
|
void* ptr = (void*)arg.ptr;
|
|
self.out('[')!;
|
|
for (usz i = 0; i < len; i++)
|
|
{
|
|
if (i != 0) self.out_substr(", ")!;
|
|
self.out_str(any { ptr, inner })!;
|
|
ptr += size;
|
|
}
|
|
return self.out(']');
|
|
case VECTOR:
|
|
if (self.print_with_function(arg)!) return;
|
|
// this is SomeType[*] so grab the "SomeType"
|
|
typeid inner = arg.type.inner;
|
|
usz size = inner.sizeof;
|
|
usz len = arg.type.len;
|
|
// Pretend this is a String
|
|
void* ptr = (void*)arg.ptr;
|
|
self.out_substr("[<")!;
|
|
for (usz i = 0; i < len; i++)
|
|
{
|
|
if (i != 0) self.out_substr(", ")!;
|
|
self.out_str(any { ptr, inner })!;
|
|
ptr += size;
|
|
}
|
|
return self.out_substr(">]");
|
|
case SUBARRAY:
|
|
if (self.print_with_function(arg)!) return;
|
|
// this is SomeType[] so grab the "SomeType"
|
|
typeid inner = arg.type.inner;
|
|
if (inner == char.typeid)
|
|
{
|
|
return self.out_substr(*(String*)arg);
|
|
}
|
|
usz size = inner.sizeof;
|
|
// Pretend this is a String
|
|
String* temp = (void*)arg.ptr;
|
|
void* ptr = (void*)temp.ptr;
|
|
usz len = temp.len;
|
|
self.out('[')!;
|
|
for (usz i = 0; i < len; i++)
|
|
{
|
|
if (i != 0) self.out_substr(", ")!;
|
|
self.out_str(any { ptr, inner })!;
|
|
ptr += size;
|
|
}
|
|
self.out(']')!;
|
|
case BOOL:
|
|
return self.out_substr(*(bool*)arg.ptr ? "true" : "false");
|
|
default:
|
|
if (self.print_with_function(arg)!) return;
|
|
return self.out_substr("Invalid type");
|
|
}
|
|
}
|
|
|
|
|
|
|
|
fn void! out_buffer_fn(char c, void *data) @private
|
|
{
|
|
BufferData *buffer_data = data;
|
|
if (buffer_data.written >= buffer_data.buffer.len) return PrintFault.BUFFER_EXCEEDED?;
|
|
buffer_data.buffer[buffer_data.written++] = c;
|
|
}
|
|
|
|
fn void! out_null_fn(char c @unused, void* data @unused) @private
|
|
{
|
|
}
|
|
|
|
fn void! out_putchar_fn(char c, void* data @unused) @private
|
|
{
|
|
libc::putchar(c);
|
|
}
|
|
|
|
fn void! out_fputchar_fn(char c, void* data) @private
|
|
{
|
|
File* f = data;
|
|
f.putc(c)!;
|
|
}
|
|
|
|
struct BufferData @private
|
|
{
|
|
char[] buffer;
|
|
usz written;
|
|
}
|
|
|
|
|
|
fn usz! Formatter.vprintf(&self, String format, any[] anys)
|
|
{
|
|
if (!self.out_fn)
|
|
{
|
|
// use null output function
|
|
self.out_fn = &out_null_fn;
|
|
}
|
|
usz format_len = format.len;
|
|
usz variant_index = 0;
|
|
for (usz i = 0; i < format_len; i++)
|
|
{
|
|
// format specifier? %[flags][width][.precision][length]
|
|
char c = format[i];
|
|
if (c != '%')
|
|
{
|
|
// no
|
|
self.out(c)!;
|
|
continue;
|
|
}
|
|
i++;
|
|
if (i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
|
|
c = format[i];
|
|
if (c == '%')
|
|
{
|
|
self.out(c)!;
|
|
continue;
|
|
}
|
|
// evaluate flags
|
|
self.flags = {};
|
|
while FLAG_EVAL: (true)
|
|
{
|
|
switch (c)
|
|
{
|
|
case '0': self.flags.zeropad = true;
|
|
case '-': self.flags.left = true;
|
|
case '+': self.flags.plus = true;
|
|
case ' ': self.flags.space = true;
|
|
case '#': self.flags.hash = true;
|
|
default: break FLAG_EVAL;
|
|
}
|
|
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
|
|
c = format[i];
|
|
}
|
|
// evaluate width field
|
|
int w = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
|
|
c = format[i];
|
|
if (w < 0)
|
|
{
|
|
self.flags.left = true;
|
|
w = -w;
|
|
}
|
|
self.width = w;
|
|
// evaluate precision field
|
|
self.prec = 0;
|
|
if (c == '.')
|
|
{
|
|
self.flags.precision = true;
|
|
if (++i >= format_len) return PrintFault.INVALID_FORMAT_STRING?;
|
|
int prec = printf_parse_format_field(anys.ptr, anys.len, &variant_index, format.ptr, format.len, &i)!;
|
|
self.prec = prec < 0 ? 0 : prec;
|
|
c = format[i];
|
|
}
|
|
|
|
// evaluate specifier
|
|
uint base = 0;
|
|
if (variant_index >= anys.len) return PrintFault.MISSING_ARG?;
|
|
any current = anys[variant_index++];
|
|
switch (c)
|
|
{
|
|
case 'd':
|
|
base = 10;
|
|
self.flags.hash = false;
|
|
case 'X' :
|
|
self.flags.uppercase = true;
|
|
nextcase;
|
|
case 'x' :
|
|
base = 16;
|
|
case 'O':
|
|
self.flags.uppercase = true;
|
|
nextcase;
|
|
case 'o' :
|
|
base = 8;
|
|
case 'B':
|
|
self.flags.uppercase = true;
|
|
nextcase;
|
|
case 'b' :
|
|
base = 2;
|
|
case 'A':
|
|
self.flags.uppercase = true;
|
|
nextcase;
|
|
case 'a':
|
|
self.atoa(float_from_any(current)!!)!;
|
|
continue;
|
|
case 'F' :
|
|
self.flags.uppercase = true;
|
|
nextcase;
|
|
case 'f':
|
|
self.ftoa(float_from_any(current)!!)!;
|
|
continue;
|
|
case 'E':
|
|
self.flags.uppercase = true;
|
|
nextcase;
|
|
case 'e':
|
|
self.etoa(float_from_any(current)!!)!;
|
|
continue;
|
|
case 'G':
|
|
self.flags.uppercase = true;
|
|
nextcase;
|
|
case 'g':
|
|
self.gtoa(float_from_any(current)!!)!;
|
|
continue;
|
|
case 'c':
|
|
self.out_char(current)!;
|
|
continue;
|
|
case 's':
|
|
self.out_str(current)!;
|
|
continue;
|
|
case 'p':
|
|
self.flags.zeropad = true;
|
|
self.flags.hash = true;
|
|
base = 16;
|
|
default:
|
|
return PrintFault.INVALID_FORMAT_STRING?;
|
|
}
|
|
if (base != 10)
|
|
{
|
|
self.flags.plus = false;
|
|
self.flags.space = false;
|
|
}
|
|
// ignore '0' flag when precision is given
|
|
if (self.flags.precision) self.flags.zeropad = false;
|
|
|
|
bool is_neg;
|
|
uint128 v = int_from_any(current, &is_neg)!!;
|
|
|
|
self.ntoa(v, is_neg, base)!;
|
|
}
|
|
// termination
|
|
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
|
|
|
|
// return written chars without terminating \0
|
|
return self.idx;
|
|
}
|
|
|
|
|
|
fn usz! Formatter.print(&self, String str)
|
|
{
|
|
if (!self.out_fn)
|
|
{
|
|
// use null output function
|
|
self.out_fn = &out_null_fn;
|
|
}
|
|
usz len = str.len;
|
|
for (usz i = 0; i < len; i++)
|
|
{
|
|
self.out(str[i])!;
|
|
}
|
|
return self.idx;
|
|
}
|