ona/source/coral/utf8.zig

187 lines
4.3 KiB
Zig
Executable File

const std = @import("std");
const io = @import("./io.zig");
const math = @import("./math.zig");
///
/// Errors that may occur during utf8-encoded int parsing.
///
pub const IntParseError = math.CheckedArithmeticError || ParseError;
///
/// Optional rules for int parsing logic to consider during parsing.
///
pub const IntParseOptions = struct {
delimiter: []const u8 = "",
};
///
/// Errors that may occur during any kind of utf8-encoded parsing.
///
pub const ParseError = error {
BadSyntax,
};
///
/// Errors that may occur during any kind of utf8-encoded printing.
///
pub const PrintError = error {
PrintFailed,
PrintIncomplete,
};
///
/// Attempts to parse a float value of type described by `float` from `utf8`.
///
/// The function returns a [ParseError] if `utf8` does not conform to the syntax of a float.
///
pub fn parse_float(comptime float: std.builtin.Type.Float, utf8: []const u8) ParseError!math.Float(float) {
// ""
if (utf8.len == 0) {
return error.BadSyntax;
}
const is_negative = utf8[0] == '-';
// "-"
if (is_negative and (utf8.len == 1)) {
return error.BadSyntax;
}
const negative_offset = @boolToInt(is_negative);
var has_decimal = utf8[negative_offset] == '.';
// "-."
if (has_decimal and (utf8.len == 2)) {
return error.BadSyntax;
}
const Float = math.Float(float);
var result: Float = 0;
var factor: Float = 1;
for (utf8[0 .. negative_offset + @boolToInt(has_decimal)]) |code| switch (code) {
'.' => {
if (has_decimal) return error.BadSyntax;
has_decimal = true;
},
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => {
if (has_decimal) factor /= 10.0;
result = ((result * 10.0) + @intToFloat(Float, code - '0'));
},
else => return error.BadSyntax,
};
return result * factor;
}
///
/// Attempts to parse an int value of type described by `int` from `utf8`, with `options` as additional rules for the
/// parsing logic to consider.
///
/// The function returns a [IntParseError] if `utf8` does not conform to the syntax of a float, does not match the rules
/// specified in `option`, or exceeds the maximum size of the int described by `int`.
///
pub fn parse_int(
comptime int: std.builtin.Type.Int,
utf8: []const u8,
options: IntParseOptions) IntParseError!math.Int(int) {
if (utf8.len == 0) {
return error.BadSyntax;
}
{
const is_negative = utf8[0] == '-';
switch (int.signedness) {
.signed => {
if (is_negative and utf8.len == 1) {
return error.BadSyntax;
}
},
.unsigned => {
if (is_negative) {
return error.BadSyntax;
}
},
}
}
var result = @as(math.Int(int), 0);
for (0 .. utf8.len) |index| {
const code = utf8[index];
switch (code) {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => {
result = try math.checked_add(try math.checked_mul(result, 10), try math.checked_sub(code, '0'));
},
else => {
if (options.delimiter.len == 0 or !io.equals(options.delimiter, utf8[index ..])) {
return error.BadSyntax;
}
},
}
}
return result;
}
///
/// Attempts to print `utf8` to `writer`.
///
/// The function returns [PrintError] if the write failed to complete partially or entirely.
///
pub fn print(writer: io.Writer, utf8: []const u8) PrintError!void {
if ((writer.invoke(utf8) orelse return error.PrintFailed) != utf8.len) {
return error.PrintIncomplete;
}
}
///
/// Attempts to print the int `value` described by `int` to `writer`.
///
/// The function returns [PrintError] if the write failed to complete partially or entirely.
///
pub fn print_int(comptime int: std.builtin.Type.Int, writer: io.Writer, value: math.Int(int)) PrintError!void {
if (value == 0) {
return try print(writer, "0");
}
// TODO: Don't make this buffer arbitrarily size cause big int types WILL overflow.
var buffer = [_]u8{0} ** 40;
var buffer_count: usize = 0;
var split_value = value;
if ((int.signedness == .unsigned) and (value < 0)) {
buffer[0] = '-';
buffer_count += 1;
}
while (split_value != 0) : (buffer_count += 1) {
const radix = 10;
buffer[buffer_count] = @intCast(u8, (split_value % radix) + '0');
split_value = (split_value / radix);
}
{
const half_buffer_count = buffer_count / 2;
var index: usize = 0;
while (index < half_buffer_count) : (index += 1) {
io.swap(u8, &buffer[index], &buffer[buffer_count - index - 1]);
}
}
try print(writer, buffer[0 .. buffer_count]);
}