Add foundations of stack trace printing
This commit is contained in:
parent
8d361634d7
commit
2218bc5c77
|
@ -1,3 +1,5 @@
|
||||||
|
const debug = @import("./debug.zig");
|
||||||
|
|
||||||
const io = @import("./io.zig");
|
const io = @import("./io.zig");
|
||||||
|
|
||||||
const math = @import("./math.zig");
|
const math = @import("./math.zig");
|
||||||
|
@ -6,6 +8,11 @@ pub const DecimalFormat = struct {
|
||||||
delimiter: []const io.Byte,
|
delimiter: []const io.Byte,
|
||||||
positive_prefix: enum {none, plus, space},
|
positive_prefix: enum {none, plus, space},
|
||||||
|
|
||||||
|
const default = DecimalFormat{
|
||||||
|
.delimiter = "",
|
||||||
|
.positive_prefix = .none,
|
||||||
|
};
|
||||||
|
|
||||||
pub fn parse(self: DecimalFormat, utf8: []const io.Byte, comptime Decimal: type) ?Decimal {
|
pub fn parse(self: DecimalFormat, utf8: []const io.Byte, comptime Decimal: type) ?Decimal {
|
||||||
if (utf8.len == 0) {
|
if (utf8.len == 0) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -104,5 +111,170 @@ pub const DecimalFormat = struct {
|
||||||
else => @compileError("`" ++ @typeName(Decimal) ++ "` cannot be formatted as a decimal string"),
|
else => @compileError("`" ++ @typeName(Decimal) ++ "` cannot be formatted as a decimal string"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn print(self: DecimalFormat, writer: io.Writer, value: anytype) PrintError!void {
|
||||||
|
if (value == 0) {
|
||||||
|
return print_string(writer, switch (self.positive_prefix) {
|
||||||
|
.none => "0",
|
||||||
|
.plus => "+0",
|
||||||
|
.space => " 0",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const ValueType = @TypeOf(value);
|
||||||
|
|
||||||
|
switch (@typeInfo(ValueType)) {
|
||||||
|
.Int => |int| {
|
||||||
|
const radix = 10;
|
||||||
|
var buffer = [_]u8{0} ** (1 + math.max(int.bits, 1));
|
||||||
|
var buffer_start = buffer.len - 1;
|
||||||
|
|
||||||
|
{
|
||||||
|
var decomposable_value = value;
|
||||||
|
|
||||||
|
while (decomposable_value != 0) : (buffer_start -= 1) {
|
||||||
|
buffer[buffer_start] = @intCast((decomposable_value % radix) + '0');
|
||||||
|
decomposable_value = (decomposable_value / radix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (int.signedness == .unsigned and value < 0) {
|
||||||
|
buffer[buffer_start] = '-';
|
||||||
|
} else {
|
||||||
|
switch (self.positive_prefix) {
|
||||||
|
.none => buffer_start += 1,
|
||||||
|
.plus => buffer[buffer_start] = '+',
|
||||||
|
.space => buffer[buffer_start] = ' ',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try print_string(writer, buffer[buffer_start ..]);
|
||||||
|
},
|
||||||
|
|
||||||
|
else => unformattableMessage(ValueType),
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub const HexadecimalFormat = struct {
|
||||||
|
delimiter: []const u8 = "",
|
||||||
|
positive_prefix: enum {none, plus, space} = .none,
|
||||||
|
casing: enum {lower, upper} = .lower,
|
||||||
|
|
||||||
|
const default = HexadecimalFormat{
|
||||||
|
.delimiter = "",
|
||||||
|
.positive_prefix = .none,
|
||||||
|
.casing = .lower,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn print(self: HexadecimalFormat, writer: io.Writer, value: anytype) PrintError!void {
|
||||||
|
// TODO: Implement.
|
||||||
|
_ = self;
|
||||||
|
_ = writer;
|
||||||
|
_ = value;
|
||||||
|
|
||||||
|
unreachable;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PrintError = error {
|
||||||
|
PrintFailed,
|
||||||
|
PrintIncomplete,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn print_string(writer: io.Writer, utf8: []const io.Byte) PrintError!void {
|
||||||
|
if ((writer.invoke(utf8) orelse return error.PrintFailed) != utf8.len) {
|
||||||
|
return error.PrintIncomplete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_formatted(writer: io.Writer, comptime format: []const u8, arguments: anytype) PrintError!void {
|
||||||
|
switch (@typeInfo(@TypeOf(arguments))) {
|
||||||
|
.Struct => |arguments_struct| {
|
||||||
|
comptime var arg_index = 0;
|
||||||
|
comptime var head = 0;
|
||||||
|
comptime var tail = 0;
|
||||||
|
|
||||||
|
inline while (tail < format.len) : (tail += 1) {
|
||||||
|
if (format[tail] == '{') {
|
||||||
|
if (tail > format.len) {
|
||||||
|
@compileError("expected an idenifier after opening `{`");
|
||||||
|
}
|
||||||
|
|
||||||
|
tail += 1;
|
||||||
|
|
||||||
|
switch (format[tail]) {
|
||||||
|
'{' => {
|
||||||
|
try print_string(writer, format[head .. (tail - 1)]);
|
||||||
|
|
||||||
|
tail += 1;
|
||||||
|
head = tail;
|
||||||
|
},
|
||||||
|
|
||||||
|
'}' => {
|
||||||
|
if (!arguments_struct.is_tuple) {
|
||||||
|
@compileError("all format specifiers must be named when using a named struct");
|
||||||
|
}
|
||||||
|
|
||||||
|
try print_string(writer, arguments[arg_index]);
|
||||||
|
|
||||||
|
arg_index += 1;
|
||||||
|
tail += 1;
|
||||||
|
head = tail;
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
if (arguments_struct.is_tuple) {
|
||||||
|
@compileError("format specifiers cannot be named when using a tuple struct");
|
||||||
|
}
|
||||||
|
|
||||||
|
try print_string(writer, format[head .. (tail - 1)]);
|
||||||
|
|
||||||
|
head = tail;
|
||||||
|
tail += 1;
|
||||||
|
|
||||||
|
if (tail >= format.len) {
|
||||||
|
@compileError("expected closing `}` or another `{` after opening `{`");
|
||||||
|
}
|
||||||
|
|
||||||
|
debug.assert(tail < format.len);
|
||||||
|
|
||||||
|
inline while (format[tail] != '}') {
|
||||||
|
tail += 1;
|
||||||
|
|
||||||
|
debug.assert(tail < format.len);
|
||||||
|
}
|
||||||
|
|
||||||
|
try print_value(writer, @field(arguments, format[head .. tail]));
|
||||||
|
|
||||||
|
tail += 1;
|
||||||
|
head = tail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
else => @compileError("`arguments` must be a struct type"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
noinline fn print_value(writer: io.Writer, value: anytype) PrintError!void {
|
||||||
|
const Value = @TypeOf(value);
|
||||||
|
|
||||||
|
return switch (@typeInfo(Value)) {
|
||||||
|
.Int => DecimalFormat.default.print(writer, value),
|
||||||
|
.Float => DecimalFormat.default.print(writer, value),
|
||||||
|
|
||||||
|
.Pointer => |pointer| switch (pointer.size) {
|
||||||
|
.One, .Many, .C => HexadecimalFormat.default.print(writer, @intFromPtr(value)),
|
||||||
|
.Slice => if (pointer.child == u8) print_string(writer, value) else @compileError(unformattableMessage(Value)),
|
||||||
|
},
|
||||||
|
|
||||||
|
else => @compileError(unformattableMessage(Value)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unformattableMessage(comptime Value: type) []const u8 {
|
||||||
|
return "type `" ++ @typeName(Value) ++ "` is not formattable with this formatter";
|
||||||
|
}
|
||||||
|
|
|
@ -456,9 +456,12 @@ pub const RuntimeEnv = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn raise(self: *RuntimeEnv, runtime_error: RuntimeError, error_message: []const u8) RuntimeError {
|
pub fn raise(self: *RuntimeEnv, runtime_error: RuntimeError, error_message: []const u8) RuntimeError {
|
||||||
if (self.err_writer.invoke(error_message) == null) {
|
// TODO: Print stack trace from state.
|
||||||
return error.SystemFailure;
|
coral.utf8.print_formatted(self.err_writer, "{name}@{line}: {message}", .{
|
||||||
}
|
.name = "???",
|
||||||
|
.line = @as(u64, 0),
|
||||||
|
.message = error_message,
|
||||||
|
}) catch return error.SystemFailure;
|
||||||
|
|
||||||
return runtime_error;
|
return runtime_error;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue