scanner: refactor to use new std.fmt features

This commit is contained in:
novakne
2021-05-23 11:06:23 +00:00
committed by Isaac Freund
parent ce545d8851
commit 55bc32c4b3
2 changed files with 143 additions and 144 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
zig-cache
zig-out

View File

@@ -1,6 +1,7 @@
const std = @import("std");
const fs = std.fs;
const mem = std.mem;
const fmtId = std.zig.fmtId;
const xml = @import("xml.zig");
@@ -298,24 +299,21 @@ const Interface = struct {
}
fn emit(interface: Interface, side: Side, namespace: []const u8, writer: anytype) !void {
var buf: [1024]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf);
try printIdentifier(fbs.writer(), case(.title, trimPrefix(interface.name)));
const title_case = fbs.getWritten();
try printIdentifier(fbs.writer(), trimPrefix(interface.name));
const snake_case = fbs.getWritten()[title_case.len..];
try writer.print(
\\pub const {s} = opaque {{
\\ pub const getInterface = common.{s}.{s}.getInterface;
, .{ title_case, namespace, snake_case });
\\pub const {[type]} = opaque {{
\\ pub const getInterface = common.{[namespace]}.{[interface]}.getInterface;
, .{
.@"type" = titleCaseTrim(interface.name),
.namespace = fmtId(namespace),
.interface = fmtId(trimPrefix(interface.name)),
});
for (interface.enums.items) |e| {
try writer.writeAll("pub const ");
try printIdentifier(writer, case(.title, e.name));
try writer.print(" = common.{s}.{s}.", .{ namespace, snake_case });
try printIdentifier(writer, case(.title, e.name));
try writer.writeAll(";\n");
try writer.print("pub const {[type]} = common.{[namespace]}.{[interface]}.{[type]};\n", .{
.@"type" = titleCase(e.name),
.namespace = fmtId(namespace),
.interface = fmtId(trimPrefix(interface.name)),
});
}
if (side == .client) {
@@ -325,16 +323,19 @@ const Interface = struct {
try writer.writeAll("};\n");
try writer.print(
\\pub fn setListener(
\\ _{s}: *{s},
\\ _{[interface]}: *{[type]},
\\ comptime T: type,
\\ _listener: fn ({s}: *{s}, event: Event, data: T) void,
\\ _listener: fn ({[interface]}: *{[type]}, event: Event, data: T) void,
\\ _data: T,
\\) callconv(.Inline) void {{
\\ const _proxy = @ptrCast(*client.wl.Proxy, _{s});
\\ const _proxy = @ptrCast(*client.wl.Proxy, _{[interface]});
\\ const _mut_data = @intToPtr(?*c_void, @ptrToInt(_data));
\\ _proxy.addDispatcher(common.Dispatcher({s}, T).dispatcher, _listener, _mut_data);
\\ _proxy.addDispatcher(common.Dispatcher({[type]}, T).dispatcher, _listener, _mut_data);
\\}}
, .{ snake_case, title_case, snake_case, title_case, snake_case, title_case });
, .{
.interface = fmtId(trimPrefix(interface.name)),
.@"type" = titleCaseTrim(interface.name),
});
}
for (interface.requests.items) |request, opcode|
@@ -344,22 +345,17 @@ const Interface = struct {
try writer.writeAll(@embedFile("client_display_functions.zig"));
} else {
try writer.print(
\\pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*{s} {{
\\ return @ptrCast(*{s}, try server.wl.Resource.create(_client, {s}, _version, _id));
\\pub fn create(_client: *server.wl.Client, _version: u32, _id: u32) !*{(tc)} {{
\\ return @ptrCast(*{[type]}, try server.wl.Resource.create(_client, {[type]}, _version, _id));
\\}}pub fn destroy(_{[interface]}: *{[type]}) void {{
\\ return @ptrCast(*server.wl.Resource, _{[interface]}).destroy();
\\}}pub fn fromLink(_link: *server.wl.list.Link) *{[type]} {{
\\ return @ptrCast(*{[type]}, server.wl.Resource.fromLink(_link));
\\}}
, .{ title_case, title_case, title_case });
try writer.print(
\\pub fn destroy(_{s}: *{s}) void {{
\\ return @ptrCast(*server.wl.Resource, _{s}).destroy();
\\}}
, .{ snake_case, title_case, snake_case });
try writer.print(
\\pub fn fromLink(_link: *server.wl.list.Link) *{s} {{
\\ return @ptrCast(*{s}, server.wl.Resource.fromLink(_link));
\\}}
, .{ title_case, title_case });
, .{
.@"type" = titleCaseTrim(interface.name),
.interface = fmtId(trimPrefix(interface.name)),
});
for ([_][2][]const u8{
.{ "getLink", "*server.wl.list.Link" },
@@ -369,20 +365,28 @@ const Interface = struct {
.{ "postNoMemory", "void" },
}) |func|
try writer.print(
\\pub fn {s}(_{s}: *{s}) {s} {{
\\ return @ptrCast(*server.wl.Resource, _{s}).{s}();
\\pub fn {[function]}(_{[interface]}: *{[type]}) {[return_type]} {{
\\ return @ptrCast(*server.wl.Resource, _{[interface]}).{[function]}();
\\}}
, .{ func[0], snake_case, title_case, func[1], snake_case, func[0] });
, .{
.function = camelCase(func[0]),
.interface = fmtId(trimPrefix(interface.name)),
.@"type" = titleCaseTrim(interface.name),
.return_type = camelCase(func[1]),
});
const has_error = for (interface.enums.items) |e| {
if (mem.eql(u8, e.name, "error")) break true;
} else false;
if (has_error) {
try writer.print(
\\pub fn postError({s}: *{s}, _err: Error, _message: [*:0]const u8) void {{
\\ return @ptrCast(*server.wl.Resource, {s}).postError(@intCast(u32, @enumToInt(_err)), _message);
\\pub fn postError({[interface]}: *{[type]}, _err: Error, _message: [*:0]const u8) void {{
\\ return @ptrCast(*server.wl.Resource, {[interface]}).postError(@intCast(u32, @enumToInt(_err)), _message);
\\}}
, .{ snake_case, title_case, snake_case });
, .{
.interface = fmtId(trimPrefix(interface.name)),
.@"type" = titleCaseTrim(interface.name),
});
}
if (interface.requests.items.len > 0) {
@@ -392,37 +396,40 @@ const Interface = struct {
@setEvalBranchQuota(2500);
try writer.print(
\\pub fn setHandler(
\\ _{s}: *{s},
\\ _{[interface]}: *{[type]},
\\ comptime T: type,
\\ handle_request: fn ({s}: *{s}, request: Request, data: T) void,
\\ comptime handle_destroy: ?fn ({s}: *{s}, data: T) void,
\\ handle_request: fn ({[interface]}: *{[type]}, request: Request, data: T) void,
\\ comptime handle_destroy: ?fn ({[interface]}: *{[type]}, data: T) void,
\\ _data: T,
\\) callconv(.Inline) void {{
\\ const _resource = @ptrCast(*server.wl.Resource, _{s});
\\ const _resource = @ptrCast(*server.wl.Resource, _{[interface]});
\\ _resource.setDispatcher(
\\ common.Dispatcher({s}, T).dispatcher,
\\ common.Dispatcher({[type]}, T).dispatcher,
\\ handle_request,
\\ @intToPtr(?*c_void, @ptrToInt(_data)),
\\ if (handle_destroy) |_handler| struct {{
\\ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {{
\\ @call(.{{ .modifier = .always_inline }}, _handler, .{{
\\ @ptrCast(*{s}, __resource),
\\ @ptrCast(*{[type]}, __resource),
\\ @intToPtr(T, @ptrToInt(__resource.getUserData())),
\\ }});
\\ }}
\\ }}._wrapper else null,
\\ );
\\}}
, .{ snake_case, title_case, snake_case, title_case, snake_case, title_case, snake_case, title_case, title_case });
, .{
.interface = fmtId(trimPrefix(interface.name)),
.@"type" = titleCaseTrim(interface.name),
});
} else {
try writer.print(
\\pub fn setHandler(
\\ _{s}: *{s},
\\ _{[interface]}: *{[type]},
\\ comptime T: type,
\\ comptime handle_destroy: ?fn ({s}: *{s}, data: T) void,
\\ comptime handle_destroy: ?fn ({[interface]}: *{[type]}, data: T) void,
\\ _data: T,
\\) callconv(.Inline) void {{
\\ const _resource = @ptrCast(*server.wl.Resource, {s});
\\ const _resource = @ptrCast(*server.wl.Resource, {[interface]});
\\ _resource.setDispatcher(
\\ null,
\\ null,
@@ -430,14 +437,17 @@ const Interface = struct {
\\ if (handle_destroy) |_handler| struct {{
\\ fn _wrapper(__resource: *server.wl.Resource) callconv(.C) void {{
\\ @call(.{{ .modifier = .always_inline }}, _handler, .{{
\\ @ptrCast(*{s}, __resource),
\\ @ptrCast(*{[type]}, __resource),
\\ @intToPtr(T, @ptrToInt(__resource.getUserData())),
\\ }});
\\ }}
\\ }}._wrapper else null,
\\ );
\\}}
, .{ snake_case, title_case, snake_case, title_case, snake_case, title_case });
, .{
.interface = fmtId(trimPrefix(interface.name)),
.@"type" = titleCaseTrim(interface.name),
});
}
for (interface.events.items) |event, opcode|
@@ -448,8 +458,7 @@ const Interface = struct {
}
fn emitCommon(interface: Interface, writer: anytype) !void {
try writer.writeAll("pub const ");
try printIdentifier(writer, trimPrefix(interface.name));
try writer.print("pub const {}", .{fmtId(trimPrefix(interface.name))});
// TODO: stop linking libwayland generated interface structs when
// https://github.com/ziglang/zig/issues/131 is implemented
@@ -484,11 +493,11 @@ const Interface = struct {
try writer.print(
\\ = struct {{
\\ extern const {s}_interface: common.Interface;
\\ extern const {[interface]s}_interface: common.Interface;
\\ pub fn getInterface() callconv(.Inline) *const common.Interface {{
\\ return &{s}_interface;
\\ return &{[interface]s}_interface;
\\ }}
, .{ interface.name, interface.name });
, .{ .interface = interface.name });
for (interface.enums.items) |e| try e.emit(writer);
try writer.writeAll("};");
@@ -574,7 +583,7 @@ const Message = struct {
//}
fn emitField(message: Message, side: Side, writer: anytype) !void {
try printIdentifier(writer, message.name);
try writer.print("{s}", .{fmtId(message.name)});
if (message.args.items.len == 0) {
try writer.writeAll(": void,");
return;
@@ -582,17 +591,13 @@ const Message = struct {
try writer.writeAll(": struct {");
for (message.args.items) |arg| {
if (side == .server and arg.kind == .new_id and arg.kind.new_id == null) {
try writer.writeAll("interface_name: [*:0]const u8, version: u32,");
try printIdentifier(writer, arg.name);
try writer.writeAll(": u32");
try writer.print("interface_name: [*:0]const u8, version: u32,{}: u32", .{fmtId(arg.name)});
} else if (side == .client and arg.kind == .new_id) {
try printIdentifier(writer, arg.name);
try writer.writeAll(": *");
try writer.print("{}: *", .{fmtId(arg.name)});
try printAbsolute(.client, writer, arg.kind.new_id.?);
std.debug.assert(!arg.allow_null);
} else {
try printIdentifier(writer, arg.name);
try writer.writeByte(':');
try writer.print("{}:", .{fmtId(arg.name)});
// See notes on NULL in doc comment for wl_message in wayland-util.h
if (side == .client and arg.kind == .object and !arg.allow_null)
try writer.writeByte('?');
@@ -606,41 +611,34 @@ const Message = struct {
fn emitFn(message: Message, side: Side, writer: anytype, interface: Interface, opcode: usize) !void {
try writer.writeAll("pub fn ");
if (side == .server) {
try writer.writeAll("send");
try printIdentifier(writer, case(.title, message.name));
try writer.print("send{}", .{titleCase(message.name)});
} else {
try printIdentifier(writer, case(.camel, message.name));
try writer.print("{}", .{camelCase(message.name)});
}
try writer.writeAll("(_");
try printIdentifier(writer, trimPrefix(interface.name));
try writer.writeAll(": *");
try printIdentifier(writer, case(.title, trimPrefix(interface.name)));
try writer.print("(_{}: *{}", .{
fmtId(trimPrefix(interface.name)),
titleCaseTrim(interface.name),
});
for (message.args.items) |arg| {
if (side == .server and arg.kind == .new_id) {
try writer.writeAll(", _");
try printIdentifier(writer, arg.name);
try writer.writeByte(':');
try writer.print(", _{s}:", .{arg.name});
if (arg.allow_null) try writer.writeByte('?');
try writer.writeByte('*');
if (arg.kind.new_id) |iface|
try printIdentifier(writer, case(.title, trimPrefix(iface)))
try writer.print("{}", .{titleCaseTrim(iface)})
else
try writer.writeAll("server.wl.Resource");
} else if (side == .client and arg.kind == .new_id) {
if (arg.kind.new_id == null) try writer.writeAll(", comptime T: type, _version: u32");
} else {
try writer.writeAll(", _");
try printIdentifier(writer, arg.name);
try writer.writeByte(':');
try writer.print(", _{s}:", .{arg.name});
try arg.emitType(side, writer);
}
}
if (side == .server or message.kind != .constructor) {
try writer.writeAll(") void {");
} else if (message.kind.constructor) |new_iface| {
try writer.writeAll(") !*");
try printIdentifier(writer, case(.title, trimPrefix(new_iface)));
try writer.writeAll("{");
try writer.print(") !*{}{{", .{titleCaseTrim(new_iface)});
} else {
try writer.writeAll(") !*T {");
}
@@ -648,8 +646,7 @@ const Message = struct {
try writer.writeAll("const _resource = @ptrCast(*server.wl.Resource,_")
else
try writer.writeAll("const _proxy = @ptrCast(*client.wl.Proxy,_");
try printIdentifier(writer, trimPrefix(interface.name));
try writer.writeAll(");");
try writer.print("{});", .{fmtId(trimPrefix(interface.name))});
if (message.args.items.len > 0) {
try writer.writeAll("var _args = [_]common.Argument{");
for (message.args.items) |arg| {
@@ -667,14 +664,13 @@ const Message = struct {
const c_type = if (arg.kind == .uint) "u32" else "i32";
try writer.print(
\\ )) {{
\\ .Enum => @intCast({s}, @enumToInt(_{s})),
\\ .Struct => @bitCast(u32, _{s}),
\\ .Enum => @intCast({[ct]s}, @enumToInt(_{[an]})),
\\ .Struct => @bitCast(u32, _{[an]}),
\\ else => unreachable,
\\ }}
, .{ c_type, arg.name, arg.name });
, .{ .ct = c_type, .an = fmtId(arg.name) });
} else {
try writer.writeByte('_');
try printIdentifier(writer, arg.name);
try writer.print("_{s}", .{arg.name});
}
try writer.writeAll("},");
},
@@ -685,8 +681,7 @@ const Message = struct {
} else {
try writer.writeAll(".{ .o = @ptrCast(*common.Object, _");
}
try printIdentifier(writer, arg.name);
try writer.writeAll(") },");
try writer.print("{s}) }},", .{arg.name});
} else {
if (new_iface == null) {
try writer.writeAll(
@@ -711,11 +706,10 @@ const Message = struct {
},
.constructor => |new_iface| {
if (new_iface) |i| {
try writer.writeAll("return @ptrCast(*");
try printIdentifier(writer, case(.title, trimPrefix(i)));
try writer.print(", try _proxy.marshalConstructor({}, &_args, ", .{opcode});
try printIdentifier(writer, case(.title, trimPrefix(i)));
try writer.writeAll(".getInterface()));");
try writer.print("return @ptrCast(*{[type]}, try _proxy.marshalConstructor({[opcode]}, &_args, {[type]}.getInterface()));", .{
.@"type" = titleCaseTrim(i),
.opcode = opcode,
});
} else {
try writer.print("return @ptrCast(*T, try _proxy.marshalConstructorVersioned({}, &_args, T.getInterface(), _version));", .{opcode});
}
@@ -813,14 +807,14 @@ const Arg = struct {
if (arg.enum_name) |name| {
if (mem.indexOfScalar(u8, name, '.')) |dot_index| {
// Turn a reference like wl_shm.format into common.wl.shm.Format
try writer.writeAll("common.");
const us_index = mem.indexOfScalar(u8, name, '_') orelse 0;
try writer.writeAll(name[0..us_index]);
try writer.writeAll(".");
try writer.writeAll(name[us_index + 1 .. dot_index + 1]);
try writer.writeAll(case(.title, name[dot_index + 1 ..]));
try writer.print("common.{s}.{s}{}", .{
name[0..us_index],
name[us_index + 1 .. dot_index + 1],
titleCase(name[dot_index + 1 ..]),
});
} else {
try writer.writeAll(case(.title, name));
try writer.print("{}", .{titleCase(name)});
}
} else if (arg.kind == .int) {
try writer.writeAll("i32");
@@ -895,8 +889,7 @@ const Enum = struct {
}
fn emit(e: Enum, writer: anytype) !void {
try writer.writeAll("pub const ");
try printIdentifier(writer, case(.title, e.name));
try writer.print("pub const {}", .{titleCase(e.name)});
if (e.bitfield) {
var entries_emitted: u8 = 0;
@@ -904,7 +897,7 @@ const Enum = struct {
for (e.entries.items) |entry| {
const value = entry.intValue();
if (value != 0 and std.math.isPowerOfTwo(value)) {
try printIdentifier(writer, entry.name);
try writer.print("{s}", .{entry.name});
if (entries_emitted == 0) {
// Align the first field to ensure the entire packed
// struct matches the alignment of a u32. This allows
@@ -930,8 +923,7 @@ const Enum = struct {
try writer.writeAll(" = extern enum(c_int) {");
for (e.entries.items) |entry| {
try printIdentifier(writer, entry.name);
try writer.print("= {s},", .{entry.value});
try writer.print("{s}= {s},", .{ fmtId(entry.name), entry.value });
}
// Always generate non-exhaustive enums to ensure forward compatability.
// Entries have been added to wl_shm.format without bumping the version.
@@ -993,46 +985,52 @@ fn trimPrefix(s: []const u8) []const u8 {
return s[mem.indexOfScalar(u8, s, '_').? + 1 ..];
}
var case_buf: [512]u8 = undefined;
fn case(out_case: enum { title, camel }, snake_case: []const u8) []const u8 {
var i: usize = 0;
var upper = out_case == .title;
for (snake_case) |ch| {
if (ch == '_') {
const Case = enum { title, camel };
fn formatCaseImpl(case: Case, comptime trim: bool) type {
return struct {
pub fn f(
bytes: []const u8,
comptime fmt: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
var upper = case == .title;
var str = if (trim) trimPrefix(bytes) else bytes;
for (str) |c| {
if (c == '_') {
upper = true;
continue;
}
case_buf[i] = if (upper) std.ascii.toUpper(ch) else ch;
i += 1;
try writer.writeByte(if (upper) std.ascii.toUpper(c) else c);
upper = false;
}
return case_buf[0..i];
}
};
}
fn titleCase(bytes: []const u8) std.fmt.Formatter(formatCaseImpl(.title, false).f) {
return .{ .data = bytes };
}
fn titleCaseTrim(bytes: []const u8) std.fmt.Formatter(formatCaseImpl(.title, true).f) {
return .{ .data = bytes };
}
fn camelCase(bytes: []const u8) std.fmt.Formatter(formatCaseImpl(.camel, false).f) {
return .{ .data = bytes };
}
fn camelCaseTrim(bytes: []const u8) std.fmt.Formatter(formatCaseImpl(.camel, true).f) {
return .{ .data = bytes };
}
fn printAbsolute(side: Side, writer: anytype, interface: []const u8) !void {
try writer.writeAll(@tagName(side));
try writer.writeByte('.');
try printIdentifier(writer, prefix(interface) orelse return error.MissingPrefix);
try writer.writeByte('.');
try printIdentifier(writer, case(.title, trimPrefix(interface)));
}
fn printIdentifier(writer: anytype, identifier: []const u8) !void {
if (isValidIdentifier(identifier))
try writer.writeAll(identifier)
else
try writer.print("@\"{s}\"", .{identifier});
}
fn isValidIdentifier(identifier: []const u8) bool {
// !keyword [A-Za-z_] [A-Za-z0-9_]*
if (identifier.len == 0) return false;
for (identifier) |ch, i| switch (ch) {
'A'...'Z', 'a'...'z', '_' => {},
'0'...'9' => if (i == 0) return false,
else => return false,
};
return std.zig.Token.getKeyword(identifier) == null;
try writer.print("{s}.{s}.{}", .{
@tagName(side),
prefix(interface) orelse return error.MissingPrefix,
titleCaseTrim(interface),
});
}
test "parsing" {