diff --git a/CMakeLists.txt b/CMakeLists.txt index a05f8f5fda..d9cf7873be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,6 +210,7 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/elf.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/empty.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/endian.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/errno.zig" DESTINATION "${ZIG_STD_DEST}") +install(FILES "${CMAKE_SOURCE_DIR}/std/fmt.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/hash_map.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/index.zig" DESTINATION "${ZIG_STD_DEST}") install(FILES "${CMAKE_SOURCE_DIR}/std/io.zig" DESTINATION "${ZIG_STD_DEST}") diff --git a/example/guess_number/main.zig b/example/guess_number/main.zig index 90a95b2e13..1b95ad907a 100644 --- a/example/guess_number/main.zig +++ b/example/guess_number/main.zig @@ -1,5 +1,6 @@ const std = @import("std"); const io = std.io; +const fmt = std.fmt; const Rand = std.rand.Rand; const os = std.os; @@ -23,7 +24,7 @@ pub fn main(args: [][]u8) -> %void { return err; }; - const guess = io.parseUnsigned(u8, line_buf[0...line_len - 1], 10) %% { + const guess = fmt.parseUnsigned(u8, line_buf[0...line_len - 1], 10) %% { %%io.stdout.printf("Invalid number.\n"); continue; }; diff --git a/std/fmt.zig b/std/fmt.zig new file mode 100644 index 0000000000..0203954d70 --- /dev/null +++ b/std/fmt.zig @@ -0,0 +1,334 @@ +const math = @import("math.zig"); +const debug = @import("debug.zig"); +const assert = debug.assert; +const mem = @import("mem.zig"); + +const max_f64_digits = 65; +const max_int_digits = 65; + +const State = enum { // TODO put inside format function and make sure the name and debug info is correct + Start, + OpenBrace, + CloseBrace, + Integer, + IntegerWidth, + Character, +}; + +/// Renders fmt string with args, calling output with slices of bytes. +/// Return false from output function and output will not be called again. +/// Returns false if output ever returned false, true otherwise. +pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool, + comptime fmt: []const u8, args: ...) -> bool +{ + comptime var start_index = 0; + comptime var state = State.Start; + comptime var next_arg = 0; + comptime var radix = 0; + comptime var uppercase = false; + comptime var width = 0; + comptime var width_start = 0; + + inline for (fmt) |c, i| { + switch (state) { + State.Start => switch (c) { + '{' => { + // TODO if you make this an if statement with && then it breaks + if (start_index < i) { + if (!output(context, fmt[start_index...i])) + return false; + } + state = State.OpenBrace; + }, + '}' => { + if (start_index < i) { + if (!output(context, fmt[start_index...i])) + return false; + } + state = State.CloseBrace; + }, + else => {}, + }, + State.OpenBrace => switch (c) { + '{' => { + state = State.Start; + start_index = i; + }, + '}' => { + if (!formatValue(args[next_arg], context, output)) + return false; + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + 'd' => { + radix = 10; + uppercase = false; + width = 0; + state = State.Integer; + }, + 'x' => { + radix = 16; + uppercase = false; + width = 0; + state = State.Integer; + }, + 'X' => { + radix = 16; + uppercase = true; + width = 0; + state = State.Integer; + }, + 'c' => { + state = State.Character; + }, + else => @compileError("Unknown format character: " ++ []u8{c}), + }, + State.CloseBrace => switch (c) { + '}' => { + state = State.Start; + start_index = i; + }, + else => @compileError("Single '}' encountered in format string"), + }, + State.Integer => switch (c) { + '}' => { + if (!formatInt(args[next_arg], radix, uppercase, width, context, output)) + return false; + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + '0' ... '9' => { + width_start = i; + state = State.IntegerWidth; + }, + else => @compileError("Unexpected character in format string: " ++ []u8{c}), + }, + State.IntegerWidth => switch (c) { + '}' => { + width = comptime %%parseUnsigned(usize, fmt[width_start...i], 10); + if (!formatInt(args[next_arg], radix, uppercase, width, context, output)) + return false; + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + '0' ... '9' => {}, + else => @compileError("Unexpected character in format string: " ++ []u8{c}), + }, + State.Character => switch (c) { + '}' => { + if (!formatAsciiChar(args[next_arg], context, output)) + return false; + next_arg += 1; + state = State.Start; + start_index = i + 1; + }, + else => @compileError("Unexpected character in format string: " ++ []u8{c}), + }, + } + } + comptime { + if (args.len != next_arg) { + @compileError("Unused arguments"); + } + if (state != State.Start) { + @compileError("Incomplete format string: " ++ fmt); + } + } + if (start_index < fmt.len) { + if (!output(context, fmt[start_index...])) + return false; + } + + return true; +} + +pub fn formatValue(value: var, context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool { + const T = @typeOf(value); + if (@isInteger(T)) { + return formatInt(value, 10, false, 0, context, output); + } else if (@isFloat(T)) { + @compileError("TODO implement formatFloat"); + } else if (@canImplicitCast([]const u8, value)) { + const casted_value = ([]const u8)(value); + return output(context, casted_value); + } else if (T == void) { + return output(context, "void"); + } else { + @compileError("Unable to format type '" ++ @typeName(T) ++ "'"); + } +} + +pub fn formatAsciiChar(c: u8, context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool { + return output(context, (&c)[0...1]); +} + +pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize, + context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool +{ + if (@typeOf(value).is_signed) { + return formatIntSigned(value, base, uppercase, width, context, output); + } else { + return formatIntUnsigned(value, base, uppercase, width, context, output); + } +} + +fn formatIntSigned(value: var, base: u8, uppercase: bool, width: usize, + context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool +{ + const uint = @intType(false, @typeOf(value).bit_count); + if (value < 0) { + const minus_sign: u8 = '-'; + if (!output(context, (&minus_sign)[0...1])) + return false; + const new_value = uint(-(value + 1)) + 1; + const new_width = if (width == 0) 0 else (width - 1); + return formatIntUnsigned(new_value, base, uppercase, new_width, context, output); + } else if (width == 0) { + return formatIntUnsigned(uint(value), base, uppercase, width, context, output); + } else { + const plus_sign: u8 = '+'; + if (!output(context, (&plus_sign)[0...1])) + return false; + const new_value = uint(value); + const new_width = if (width == 0) 0 else (width - 1); + return formatIntUnsigned(new_value, base, uppercase, new_width, context, output); + } +} + +fn formatIntUnsigned(value: var, base: u8, uppercase: bool, width: usize, + context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool +{ + // max_int_digits accounts for the minus sign. when printing an unsigned + // number we don't need to do that. + var buf: [max_int_digits - 1]u8 = undefined; + var a = value; + var index: usize = buf.len; + + while (true) { + const digit = a % base; + index -= 1; + buf[index] = digitToChar(u8(digit), uppercase); + a /= base; + if (a == 0) + break; + } + + const digits_buf = buf[index...]; + const padding = if (width > digits_buf.len) (width - digits_buf.len) else 0; + + if (padding > index) { + const zero_byte: u8 = '0'; + var leftover_padding = padding - index; + while (true) { + if (!output(context, (&zero_byte)[0...1])) + return false; + leftover_padding -= 1; + if (leftover_padding == 0) + break; + } + mem.set(u8, buf[0...index], '0'); + return output(context, buf); + } else { + const padded_buf = buf[index - padding...]; + mem.set(u8, padded_buf[0...padding], '0'); + return output(context, padded_buf); + } +} + +pub fn formatIntBuf(out_buf: []u8, value: var, base: u8, uppercase: bool, width: usize) -> usize { + var context = FormatIntBuf { + .out_buf = out_buf, + .index = 0, + }; + _ = formatInt(value, base, uppercase, width, &context, formatIntCallback); + return context.index; +} +const FormatIntBuf = struct { + out_buf: []u8, + index: usize, +}; +fn formatIntCallback(context: &FormatIntBuf, bytes: []const u8) -> bool { + mem.copy(u8, context.out_buf[context.index...], bytes); + context.index += bytes.len; + return true; +} + +pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) -> %T { + var x: T = 0; + + for (buf) |c| { + const digit = %return charToDigit(c, radix); + x = %return math.mulOverflow(T, x, radix); + x = %return math.addOverflow(T, x, digit); + } + + return x; +} + +error InvalidChar; +fn charToDigit(c: u8, radix: u8) -> %u8 { + const value = switch (c) { + '0' ... '9' => c - '0', + 'A' ... 'Z' => c - 'A' + 10, + 'a' ... 'z' => c - 'a' + 10, + else => return error.InvalidChar, + }; + + if (value >= radix) + return error.InvalidChar; + + return value; +} + +fn digitToChar(digit: u8, uppercase: bool) -> u8 { + return switch (digit) { + 0 ... 9 => digit + '0', + 10 ... 35 => digit + ((if (uppercase) u8('A') else u8('a')) - 10), + else => @unreachable(), + }; +} + +fn testBufPrintInt() { + @setFnTest(this); + + var buffer: [max_int_digits]u8 = undefined; + const buf = buffer[0...]; + assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); + assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678")); + assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e")); + assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E")); + + assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678")); + + assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666")); + assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234")); + assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234")); + + assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42")); + assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42")); +} + +fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) -> []u8 { + return buf[0...formatIntBuf(buf, value, base, uppercase, width)]; +} + +fn testParseU64DigitTooBig() { + @setFnTest(this); + + parseUnsigned(u64, "123a", 10) %% |err| { + if (err == error.InvalidChar) return; + @unreachable(); + }; + @unreachable(); +} + +fn testParseUnsignedComptime() { + @setFnTest(this); + + comptime { + assert(%%parseUnsigned(usize, "2", 10) == 2); + } +} diff --git a/std/index.zig b/std/index.zig index b4aeb410b2..b4a86f0f9d 100644 --- a/std/index.zig +++ b/std/index.zig @@ -1,20 +1,21 @@ -pub const rand = @import("rand.zig"); -pub const io = @import("io.zig"); -pub const os = @import("os.zig"); -pub const math = @import("math.zig"); pub const cstr = @import("cstr.zig"); -pub const sort = @import("sort.zig"); -pub const net = @import("net.zig"); -pub const list = @import("list.zig"); -pub const hash_map = @import("hash_map.zig"); -pub const mem = @import("mem.zig"); pub const debug = @import("debug.zig"); +pub const fmt = @import("fmt.zig"); +pub const hash_map = @import("hash_map.zig"); +pub const io = @import("io.zig"); +pub const list = @import("list.zig"); +pub const math = @import("math.zig"); +pub const mem = @import("mem.zig"); +pub const net = @import("net.zig"); +pub const os = @import("os.zig"); +pub const rand = @import("rand.zig"); +pub const sort = @import("sort.zig"); pub const linux = switch(@compileVar("os")) { Os.linux => @import("linux.zig"), - else => null_import, + else => empty_import, }; pub const darwin = switch(@compileVar("os")) { Os.darwin => @import("darwin.zig"), - else => null_import, + else => empty_import, }; -const null_import = @import("empty.zig"); +pub const empty_import = @import("empty.zig"); diff --git a/std/io.zig b/std/io.zig index 6f3b63f387..70c1d5b97d 100644 --- a/std/io.zig +++ b/std/io.zig @@ -11,6 +11,7 @@ const assert = debug.assert; const os = @import("os.zig"); const mem = @import("mem.zig"); const Buffer0 = @import("cstr.zig").Buffer0; +const fmt = @import("fmt.zig"); pub const stdin_fileno = 0; pub const stdout_fileno = 1; @@ -61,8 +62,6 @@ error Unseekable; error Eof; const buffer_size = 4 * 1024; -const max_f64_digits = 65; -const max_int_digits = 65; pub const OpenRead = 0b0001; pub const OpenWrite = 0b0010; @@ -81,173 +80,46 @@ pub const OutStream = struct { } pub fn write(self: &OutStream, bytes: []const u8) -> %void { - var src_bytes_left = bytes.len; var src_index: usize = 0; - const dest_space_left = self.buffer.len - self.index; - while (src_bytes_left > 0) { - const copy_amt = math.min(dest_space_left, src_bytes_left); - @memcpy(&self.buffer[self.index], &bytes[src_index], copy_amt); + while (src_index < bytes.len) { + const dest_space_left = self.buffer.len - self.index; + const copy_amt = math.min(dest_space_left, bytes.len - src_index); + mem.copy(u8, self.buffer[self.index...], bytes[src_index...src_index + copy_amt]); self.index += copy_amt; + assert(self.index <= self.buffer.len); if (self.index == self.buffer.len) { %return self.flush(); } - src_bytes_left -= copy_amt; + src_index += copy_amt; } } - const State = enum { // TODO put inside printf function and make sure the name and debug info is correct - Start, - OpenBrace, - CloseBrace, - Integer, - IntegerWidth, - Character, - }; - /// Calls print and then flushes the buffer. pub fn printf(self: &OutStream, comptime format: []const u8, args: ...) -> %void { - comptime var start_index = 0; - comptime var state = State.Start; - comptime var next_arg = 0; - comptime var radix = 0; - comptime var uppercase = false; - comptime var width = 0; - comptime var width_start = 0; - - inline for (format) |c, i| { - switch (state) { - State.Start => switch (c) { - '{' => { - if (start_index < i) %return self.write(format[start_index...i]); - state = State.OpenBrace; - }, - '}' => { - if (start_index < i) %return self.write(format[start_index...i]); - state = State.CloseBrace; - }, - else => {}, - }, - State.OpenBrace => switch (c) { - '{' => { - state = State.Start; - start_index = i; - }, - '}' => { - %return self.printValue(args[next_arg]); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - 'd' => { - radix = 10; - uppercase = false; - width = 0; - state = State.Integer; - }, - 'x' => { - radix = 16; - uppercase = false; - width = 0; - state = State.Integer; - }, - 'X' => { - radix = 16; - uppercase = true; - width = 0; - state = State.Integer; - }, - 'c' => { - state = State.Character; - }, - else => @compileError("Unknown format character: " ++ []u8{c}), - }, - State.CloseBrace => switch (c) { - '}' => { - state = State.Start; - start_index = i; - }, - else => @compileError("Single '}' encountered in format string"), - }, - State.Integer => switch (c) { - '}' => { - %return self.printInt(args[next_arg], radix, uppercase, width); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0' ... '9' => { - width_start = i; - state = State.IntegerWidth; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.IntegerWidth => switch (c) { - '}' => { - width = comptime %%parseUnsigned(usize, format[width_start...i], 10); - %return self.printInt(args[next_arg], radix, uppercase, width); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - '0' ... '9' => {}, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - State.Character => switch (c) { - '}' => { - %return self.printAsciiChar(args[next_arg]); - next_arg += 1; - state = State.Start; - start_index = i + 1; - }, - else => @compileError("Unexpected character in format string: " ++ []u8{c}), - }, - } - } - comptime { - if (args.len != next_arg) { - @compileError("Unused arguments"); - } - if (state != State.Start) { - @compileError("Incomplete format string: " ++ format); - } - } - if (start_index < format.len) { - %return self.write(format[start_index...format.len]); - } + %return self.print(format, args); %return self.flush(); } - pub fn printValue(self: &OutStream, value: var) -> %void { - const T = @typeOf(value); - if (@isInteger(T)) { - return self.printInt(value, 10, false, 0); - } else if (@isFloat(T)) { - return self.printFloat(T, value); - } else if (@canImplicitCast([]const u8, value)) { - const casted_value = ([]const u8)(value); - return self.write(casted_value); - } else if (T == void) { - return self.write("void"); - } else { - @compileError("Unable to print type '" ++ @typeName(T) ++ "'"); - } + /// Does not flush the buffer. + pub fn print(self: &OutStream, comptime format: []const u8, args: ...) -> %void { + var context = PrintContext { + .self = self, + .result = {}, + }; + _ = fmt.format(&context, printOutput, format, args); + return context.result; } - - pub fn printInt(self: &OutStream, x: var, base: u8, uppercase: bool, width: usize) -> %void { - if (self.index + max_int_digits >= self.buffer.len) { - %return self.flush(); - } - const amt_printed = bufPrintInt(self.buffer[self.index...], x, base, uppercase, width); - self.index += amt_printed; - } - - pub fn printAsciiChar(self: &OutStream, c: u8) -> %void { - if (self.index + 1 >= self.buffer.len) { - %return self.flush(); - } - self.buffer[self.index] = c; - self.index += 1; + const PrintContext = struct { + self: &OutStream, + result: %void, + }; + fn printOutput(context: &PrintContext, bytes: []const u8) -> bool { + context.self.write(bytes) %% |err| { + context.result = err; + return false; + }; + return true; } pub fn flush(self: &OutStream) -> %void { @@ -506,90 +378,6 @@ pub const InStream = struct { } }; -pub fn parseUnsigned(comptime T: type, buf: []const u8, radix: u8) -> %T { - var x: T = 0; - - for (buf) |c| { - const digit = %return charToDigit(c, radix); - x = %return math.mulOverflow(T, x, radix); - x = %return math.addOverflow(T, x, digit); - } - - return x; -} - -error InvalidChar; -fn charToDigit(c: u8, radix: u8) -> %u8 { - const value = switch (c) { - '0' ... '9' => c - '0', - 'A' ... 'Z' => c - 'A' + 10, - 'a' ... 'z' => c - 'a' + 10, - else => return error.InvalidChar, - }; - - if (value >= radix) - return error.InvalidChar; - - return value; -} - -fn digitToChar(digit: u8, uppercase: bool) -> u8 { - return switch (digit) { - 0 ... 9 => digit + '0', - 10 ... 35 => digit + ((if (uppercase) u8('A') else u8('a')) - 10), - else => @unreachable(), - }; -} - -/// Guaranteed to not use more than max_int_digits -pub fn bufPrintInt(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize { - if (@typeOf(x).is_signed) - bufPrintSigned(out_buf, x, base, uppercase, width) - else - bufPrintUnsigned(out_buf, x, base, uppercase, width) -} - -fn bufPrintSigned(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize { - const uint = @intType(false, @typeOf(x).bit_count); - if (x < 0) { - out_buf[0] = '-'; - const new_value = uint(-(x + 1)) + 1; - const new_width = if (width == 0) 0 else (width - 1); - return 1 + bufPrintUnsigned(out_buf[1...], new_value, base, uppercase, new_width); - } else if (width == 0) { - return bufPrintUnsigned(out_buf, uint(x), base, uppercase, width); - } else { - out_buf[0] = '+'; - const new_value = uint(x); - const new_width = if (width == 0) 0 else (width - 1); - return 1 + bufPrintUnsigned(out_buf[1...], new_value, base, uppercase, new_width); - } -} - -fn bufPrintUnsigned(out_buf: []u8, x: var, base: u8, uppercase: bool, width: usize) -> usize { - // max_int_digits accounts for the minus sign. when printing an unsigned - // number we don't need to do that. - var buf: [max_int_digits - 1]u8 = undefined; - var a = x; - var index: usize = buf.len; - - while (true) { - const digit = a % base; - index -= 1; - buf[index] = digitToChar(u8(digit), uppercase); - a /= base; - if (a == 0) - break; - } - - const src_buf = buf[index...]; - const padding = if (width > src_buf.len) (width - src_buf.len) else 0; - - mem.set(u8, out_buf[0...padding], '0'); - mem.copy(u8, out_buf[padding...], src_buf); - return src_buf.len + padding; -} - pub fn openSelfExe(stream: &InStream) -> %void { switch (@compileVar("os")) { Os.linux => { @@ -602,45 +390,3 @@ pub fn openSelfExe(stream: &InStream) -> %void { else => @compileError("unsupported os"), } } - -fn bufPrintIntToSlice(buf: []u8, value: var, base: u8, uppercase: bool, width: usize) -> []u8 { - return buf[0...bufPrintInt(buf, value, base, uppercase, width)]; -} - -fn testParseU64DigitTooBig() { - @setFnTest(this); - - parseUnsigned(u64, "123a", 10) %% |err| { - if (err == error.InvalidChar) return; - @unreachable(); - }; - @unreachable(); -} - -fn testParseUnsignedComptime() { - @setFnTest(this); - - comptime { - assert(%%parseUnsigned(usize, "2", 10) == 2); - } -} - -fn testBufPrintInt() { - @setFnTest(this); - - var buffer: [max_int_digits]u8 = undefined; - const buf = buffer[0...]; - assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 2, false, 0), "-101111000110000101001110")); - assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 10, false, 0), "-12345678")); - assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, false, 0), "-bc614e")); - assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-12345678), 16, true, 0), "-BC614E")); - - assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(12345678), 10, true, 0), "12345678")); - - assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(666), 10, false, 6), "000666")); - assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 6), "001234")); - assert(mem.eql(u8, bufPrintIntToSlice(buf, u32(0x1234), 16, false, 1), "1234")); - - assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(42), 10, false, 3), "+42")); - assert(mem.eql(u8, bufPrintIntToSlice(buf, i32(-42), 10, false, 3), "-42")); -} diff --git a/test/cases/enum_with_members.zig b/test/cases/enum_with_members.zig index da9ff0f40c..36c7b5d175 100644 --- a/test/cases/enum_with_members.zig +++ b/test/cases/enum_with_members.zig @@ -1,6 +1,6 @@ const assert = @import("std").debug.assert; const mem = @import("std").mem; -const io = @import("std").io; +const fmt = @import("std").fmt; const ET = enum { SINT: i32, @@ -8,8 +8,8 @@ const ET = enum { pub fn print(a: &const ET, buf: []u8) -> %usize { return switch (*a) { - ET.SINT => |x| { io.bufPrintInt(buf, x, 10, false, 0) }, - ET.UINT => |x| { io.bufPrintInt(buf, x, 10, false, 0) }, + ET.SINT => |x| { fmt.formatIntBuf(buf, x, 10, false, 0) }, + ET.UINT => |x| { fmt.formatIntBuf(buf, x, 10, false, 0) }, } } };