output: use a comptime type-independent writer

all rendering functions output using Ais (AutoIndentingStream) which
writes to an std.fs.File.Writer but some commented out functions
like renderArrayInit need to use Ais to temporary output a partial
expression into an std.ArrayList(u8) buffer, making a new type of Ais
which renderExpression func cannot accept.

the new TypeErasedWriter abstracts the underlying writer away and so Ais
now always outputs to the same TypeErasedWriter.Writer type. in other
words, a single type of Ais should now satisfy all render functions.
0.10
alex 2 years ago
parent b2d59f9f0e
commit 56fe08fa7a

@ -42,7 +42,7 @@ pub fn main() !void {
// output all results to stdout
var auto_indenting_stream = output.Ais{
.indent_delta = 4,
.underlying_writer = stdout,
.underlying_writer = output.TypeErasedWriter.init(&stdout),
};
const ais = &auto_indenting_stream;

@ -2,7 +2,7 @@
//! in fact, most of it is a copy of the original with just a few modifications:
//! - rename renderMember to renderPubMember to emphasize this is about public types only
//! - commented out unused code; it needs not render type definitions
//! - AutoIdentingStream outputs directly to a File.Writer; return anyerror
//! - AutoIndentingStream outputs to a type-independent writer; returns anyerror
//! instead of Ast.RenderError
const std = @import("std");
@ -15,8 +15,8 @@ const Ast = std.zig.Ast;
const log = std.log.scoped(.render);
/// Ais is an AutoIdentingStream suitable for writing directly to stdout or stderr.
pub const Ais = AutoIndentingStream(std.fs.File.Writer);
/// Ais is writer type-independent AutoIndentingStream used by all renderXxx funcs.
pub const Ais = AutoIndentingStream(TypeErasedWriter.Writer);
/// renderPubMember prints the given declaration using ais.
/// it outputs anything only if the declaration is public.
@ -2497,7 +2497,7 @@ fn hasSameLineComment(tree: Ast, token_index: Ast.TokenIndex) bool {
// return false;
//}
fn writeFixingWhitespace(writer: std.fs.File.Writer, slice: []const u8) anyerror!void {
fn writeFixingWhitespace(writer: TypeErasedWriter.Writer, slice: []const u8) anyerror!void {
for (slice) |byte| switch (byte) {
'\t' => try writer.writeAll(" " ** 4),
'\r' => {},
@ -2761,3 +2761,59 @@ fn AutoIndentingStream(comptime UnderlyingWriter: type) type {
}
};
}
/// TypeErasedWriter abstracts a writer type away so that functions can
/// can be more relaxed, without requiring a comptime writer.
///
/// example usage with a file writer:
///
/// const stdio = std.io.getStdOut().writer();
/// var writer = TypeEraseWrite.init(&stdio);
///
/// with an array buffer:
///
/// var buf = std.ArrayList(u8).init(allocator);
/// var writer = TypeEraseWrite.init(&buf.writer());
///
/// its std io type is TypeErasedWriter.Writer.
pub const TypeErasedWriter = struct {
/// a @ptrToInt-cast address to the baseWriter provided during init.
writerAddr: usize,
/// contains write func implementation which invokes baseWriter at writerAddr.
vtable: *const VTable,
/// Writer is the standard io writer type of TypeErasedWriter.
pub const Writer = std.io.Writer(@This(), WriteError, write);
const WriteError = anyerror;
const VTable = struct {
writeFn: fn (usize, []const u8) WriteError!usize,
};
/// pointer is expected to be of an std.io.Writer type.
/// see TypeErasedWriter for usage examples.
pub fn init(baseWriter: anytype) Writer {
const PtrType = @TypeOf(baseWriter);
const ptrinfo = @typeInfo(PtrType);
std.debug.assert(ptrinfo == .Pointer);
std.debug.assert(ptrinfo.Pointer.size == .One); // single-item pointer
const gen = struct {
fn writeImpl(writerAddr: usize, bytes: []const u8) WriteError!usize {
const w = @intToPtr(PtrType, writerAddr);
return w.write(bytes);
}
const vtable = VTable{ .writeFn = writeImpl };
};
return .{
.context = TypeErasedWriter{
.writerAddr = @ptrToInt(baseWriter),
.vtable = &gen.vtable,
},
};
}
fn write(self: @This(), bytes: []const u8) WriteError!usize {
return self.vtable.writeFn(self.writerAddr, bytes);
}
};

Loading…
Cancel
Save