~sircmpwn/public-inbox

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
3 2

[PATCH hare-wayland] cmd/scanner: Use strings::template and merge interface+request+event

Details
Message ID
<20230128103003.23298-1-contact+sr.ht@hacktivis.me>
DKIM signature
missing
Download raw message
Patch: +273 -145
From: "Haelwenn (lanodan) Monnier" <contact@hacktivis.me>

---
 cmd/scanner/main.ha | 418 +++++++++++++++++++++++++++++---------------
 1 file changed, 273 insertions(+), 145 deletions(-)

diff --git a/cmd/scanner/main.ha b/cmd/scanner/main.ha
index 379e196..3dc6481 100644
--- a/cmd/scanner/main.ha
+++ b/cmd/scanner/main.ha
@@ -1,9 +1,163 @@
use fmt;
use io;
use os;
use strings;
use strings::template;
use strio;
use wayland;
use wayland::scanner;

// generate function
def generate_src: str = `// Generated code for '$name' protocol
use bufio;
use fmt;
use io;
use wayland;
`;

// interface function
def interface_src: str = `
export type $ifname = struct {
	object: wayland::object,
	listeners: []*$ifname_listener,
};

export fn $ifname_obj_init(c: *wayland::client) *wayland::object = {
	let o = wayland::object{id = 0, client = c, iface = &$ifname_interface};
	let r = alloc($ifname{object = o, listeners = [] });
	return r;
};


export fn $ifname_obj_finish(o: *wayland::object) void = {
	let o = o: *$ifname;
	for (let i = 0z; i < len(o.listeners); i += 1) {
		free(o.listeners[i]);
	};
	free(o.listeners);
	free(o);
};

export const $ifname_interface: wayland::interface = wayland::interface {
	name = "$ifacename",
	version = $ifaceversion,
	requests = [$requests
	],
	events = [$events
	],
	enums = [
	],
	init = &$ifname_obj_init,
	finish = &$ifname_obj_finish,
};
`;

def request_src: str = `
		wayland::request {
			name = "$name",
			since = $since,
			type_ = "$type",
			args = [$args
			],
		},`;

def event_src: str = `
		wayland::event {
			name = "$name",
			since = $since,
			type_ = "$type",
			args = [$args
			],
			cb = &$ifname_$name_unmarshal,
		},`;

def arg_src: str = `
				wayland::argument {
					name = "$name",
					type_ = wayland::argtype::$type,
					enum_ = "$enum",
					interface = "$interface",
					allownull = $allownull,
				},`;

// request_function function
def request_function_src: str = `
export fn $ifname_$name(obj: *$ifname$fnargs) $rettype = {
	let buf = bufio::dynamic(io::mode::RDWR);$inner
	match (wayland::queue_request(obj, $opcode, bufio::buffer(&buf))) {
	case void => void;
	case let e: io::error => fmt::fatal("I/O error: {}", io::strerror(e));
	};
$return};
`;

// event_callback function
def eventcallback_src: str = `
export type $ifname_$evname_callback = *fn(userdata: nullable *void, obj: *$ifname$argscb) void;

export fn $ifname_$evname_unmarshal(id: wayland::object_id, conn: *wayland::client) void = {
	let obj = match (wayland::registry_get(&conn.registry, id)) {
	case let o: *wayland::object =>
		yield o: *$ifname;
	case null =>
		fmt::fatal("callback for unregistered object id");
	};
	// rbuffer includes header, start bufio::fixed from arguments
	let buf = bufio::fixed(conn.mbuf[8..], io::mode::READ);$argsfn
	for (let i = 0z; i < len(obj.listeners); i += 1) {
		match (obj.listeners[i].$evname) {
		case let cb: $ifname_$evname_callback =>
			cb(obj.listeners[i].userdata, obj$evargs);
		case void =>
			continue;
		};
	};
};
`;

// listener function
def listener_src: str = `
export type $ifname_listener = struct {
	userdata: nullable *void,
$events};

export fn $ifname_add_listener(o: *$ifname, listener: $ifname_listener) void = {
	let copy: *$ifname_listener = alloc(listener);
	append(o.listeners, copy);
};
`;

let generate_template: template::template = [];
let interface_template: template::template = [];
let arg_template: template::template = [];
let request_template: template::template = [];
let event_template: template::template = [];
let request_function_template: template::template = [];
let eventcallback_template: template::template = [];
let listener_template: template::template = [];

@init fn init_templates() void = {
	generate_template = template::compile(generate_src)!;
	interface_template = template::compile(interface_src)!;
	arg_template = template::compile(arg_src)!;
	request_template = template::compile(request_src)!;
	event_template = template::compile(event_src)!;
	request_function_template = template::compile(request_function_src)!;
	eventcallback_template = template::compile(eventcallback_src)!;
	listener_template = template::compile(listener_src)!;
};

@fini fn fini_templates() void = {
	template::finish(&generate_template);
	template::finish(&interface_template);
	template::finish(&arg_template);
	template::finish(&request_template);
	template::finish(&event_template);
	template::finish(&request_function_template);
	template::finish(&eventcallback_template);
	template::finish(&listener_template);
};

@noreturn fn usage() void =
	fmt::fatalf("Usage: {} <client|server> < input.xml > output.ha",
		os::args[0]);
@@ -22,11 +176,8 @@ fn generate() void = {
	};
	defer scanner::finish(&proto);

	fmt::printfln("// Generated code for '{}' protocol", proto.name)!;
	fmt::println("use bufio;")!;
	fmt::println("use fmt;")!;
	fmt::println("use io;")!;
	fmt::println("use wayland;")!;
	template::execute(&generate_template, os::stdout, ("name", proto.name))!;

	for (let i = 0z; i < len(proto.interfaces); i += 1) {
		interface(&proto.interfaces[i]);
		functions(&proto.interfaces[i]);
@@ -44,93 +195,69 @@ fn ifname(iface: (*scanner::interface | str)) str = {
};

fn interface(iface: *scanner::interface) void = {
	fmt::println()!;
	fmt::printfln("export type {} = struct {{", ifname(iface))!;
	fmt::printfln("\tobject: wayland::object,")!;
	fmt::printfln("\tlisteners: []*{}_listener,", ifname(iface))!;
	fmt::printfln("}};")!;
	fmt::println()!;
	fmt::printfln("export fn {}_obj_init(c: *wayland::client) *wayland::object = {{", ifname(iface))!;
	fmt::printfln("\tlet o = wayland::object{{id = 0, client = c, iface = &{}_interface}};", ifname(iface))!;
	fmt::printfln("\tlet r = alloc({}{{object = o, listeners = [] }});", ifname(iface))!;
	fmt::printfln("\treturn r;")!;
	fmt::printfln("}};")!;
	fmt::println()!;
	fmt::println()!;
	fmt::printfln("export fn {}_obj_finish(o: *wayland::object) void = {{", ifname(iface))!;
	fmt::printfln("\tlet o = o: *{};", ifname(iface))!;
	fmt::printfln("\tfor (let i = 0z; i < len(o.listeners); i += 1) {{")!;
	fmt::printfln("\t\tfree(o.listeners[i]);")!;
	fmt::printfln("\t}};")!;
	fmt::printfln("\tfree(o.listeners);")!;
	fmt::printfln("\tfree(o);")!;
	fmt::printfln("}};")!;
	fmt::println()!;
	fmt::printfln("export const {}_interface: wayland::interface = wayland::interface {{", ifname(iface))!;
	fmt::printfln("\tname = \"{}\",", iface.name)!; // TODO: escape me
	fmt::printfln("\tversion = {},", iface.version)!;
	fmt::printfln("\trequests = [")!;
	let requests = strio::dynamic();
	defer io::close(&requests)!;

	for (let i = 0z; i < len(iface.requests); i += 1) {
		request(iface, &iface.requests[i]);
		const req = iface.requests[i];
		let args = strio::dynamic();
		defer io::close(&args)!;

		for (let i = 0z; i < len(req.args); i += 1) {
			const arg = req.args[i];
			template::execute(&arg_template, &args,
				("name", arg.name),
				("type", scanner::argtype_to_str(arg.type_)),
				("enum", arg.enum_),
				("interface", arg.interface),
				("allownull", arg.allownull),
			)!;
		};

		template::execute(&request_template, &requests,
			("name", req.name),
			("since", req.since),
			("type", req.type_),
			("args", strio::string(&args)),
		)!;
	};
	fmt::printfln("\t],")!;
	fmt::printfln("\tevents = [")!;

	let events = strio::dynamic();
	defer io::close(&events)!;

	for (let i = 0z; i < len(iface.events); i += 1) {
		event(iface, &iface.events[i]);
	};
	fmt::printfln("\t],")!;
	fmt::printfln("\tenums = [")!;
	fmt::printfln("\t],")!;
	fmt::printfln("\tinit = &{}_obj_init,", ifname(iface))!;
	fmt::printfln("\tfinish = &{}_obj_finish,", ifname(iface))!;
	fmt::printfln("}};")!;
};
		const ev = iface.events[i];
		let args = strio::dynamic();
		defer io::close(&args)!;

fn request(iface: *scanner::interface, req: *scanner::request) void = {
	fmt::printfln("\t\twayland::request {{")!;
	fmt::printfln("\t\t\tname = \"{}\",", req.name)!;
	fmt::printfln("\t\t\tsince = {},", req.since)!;
	fmt::printfln("\t\t\ttype_ = \"{}\",", req.type_)!;
	fmt::printfln("\t\t\targs = [")!;
	for (let i = 0z; i < len(req.args); i += 1) {
		req_arg(iface, req, &req.args[i]);
	};
	fmt::printfln("\t\t\t],")!;
	fmt::printfln("\t\t}},")!;
};
		for (let i = 0z; i < len(ev.args); i += 1) {
			const arg = ev.args[i];

fn req_arg(iface: *scanner::interface, req: *scanner::request, arg: *scanner::argument) void = {
	fmt::printfln("\t\t\t\twayland::argument {{")!;
	fmt::printfln("\t\t\t\t\tname = \"{}\",", arg.name)!;
	fmt::printfln("\t\t\t\t\ttype_ = wayland::argtype::{},", scanner::argtype_to_str(arg.type_))!;
	fmt::printfln("\t\t\t\t\tenum_ = \"{}\",", arg.enum_)!;
	fmt::printfln("\t\t\t\t\tinterface = \"{}\",", arg.interface)!;
	fmt::printfln("\t\t\t\t\tallownull = {},", arg.allownull)!;
	fmt::printfln("\t\t\t\t}},")!;
};
			template::execute(&arg_template, &args,
				("name", arg.name),
				("type", scanner::argtype_to_str(arg.type_)),
				("enum", arg.enum_),
				("interface", arg.interface),
				("allownull", arg.allownull),
			)!;
		};

fn event(iface: *scanner::interface, ev: *scanner::event) void = {
	fmt::printfln("\t\twayland::event {{")!;
	fmt::printfln("\t\t\tname = \"{}\",", ev.name)!;
	fmt::printfln("\t\t\tsince = {},", ev.since)!;
	fmt::printfln("\t\t\ttype_ = \"{}\",", ev.type_)!;
	fmt::printfln("\t\t\targs = [")!;
	for (let i = 0z; i < len(ev.args); i += 1) {
		ev_arg(iface, ev, &ev.args[i]);
		template::execute(&event_template, &events,
			("name", ev.name),
			("since", ev.since),
			("type", ev.type_),
			("ifname", ifname(iface)),
			("args", strio::string(&args)),
		)!;
	};
	fmt::printfln("\t\t\t],")!;
	fmt::printfln("\t\t\tcb = &{}_{}_unmarshal,", ifname(iface), ev.name)!;
	fmt::printfln("\t\t}},")!;
};

fn ev_arg(iface: *scanner::interface, ev: *scanner::event, arg: *scanner::argument) void = {
	fmt::printfln("\t\t\t\twayland::argument {{")!;
	fmt::printfln("\t\t\t\t\tname = \"{}\",", arg.name)!;
	fmt::printfln("\t\t\t\t\ttype_ = wayland::argtype::{},", scanner::argtype_to_str(arg.type_))!;
	fmt::printfln("\t\t\t\t\tenum_ = \"{}\",", arg.enum_)!;
	fmt::printfln("\t\t\t\t\tinterface = \"{}\",", arg.interface)!;
	fmt::printfln("\t\t\t\t\tallownull = {},", arg.allownull)!;
	fmt::printfln("\t\t\t\t}},")!;
	template::execute(&interface_template, os::stdout,
		("ifname", ifname(iface)),
		("ifacename", iface.name), // TODO: escape me
		("ifaceversion", iface.version),
		("requests", strio::string(&requests)),
		("events", strio::string(&events)),
	)!;
};

fn functions(iface: *scanner::interface) void = {
@@ -151,113 +278,114 @@ fn request_function(iface: *scanner::interface, req: *scanner::request, opcode:
	let ret_interface = "";
	//let payload_size = 0z;

	fmt::println()!;
	fmt::printf("export fn {}_{}(obj: *{}", ifname(iface), req.name, ifname(iface))!;
	let fnargs = strio::dynamic();
	defer io::close(&fnargs)!;

	for (let i = 0z; i < len(req.args); i += 1) {
		if (req.args[i].type_ == wayland::scanner::argtype::NEW_ID) {
			if (req.args[i].interface == "") {
				// returned object type depends on user input
				ret_interface = "wayland::object";
				fmt::printf(", iface: *wayland::interface, version: u32")!;
				fmt::fprintf(&fnargs, ", iface: *wayland::interface, version: u32")!;
			} else {
				ret_interface = ifname(req.args[i].interface);
			};
		} else {
			fmt::printf(", {}_: {}", req.args[i].name, scanner::argtype_to_type(req.args[i].type_))!;
			fmt::fprintf(&fnargs, ", {}_: {}", req.args[i].name, scanner::argtype_to_type(req.args[i].type_))!;
		};
	};
	if (ret_interface == "") {
		fmt::printfln(") void = {{")!;
	const rettype = if (ret_interface == "") {
		yield strings::dup("void");
	} else {
		fmt::printfln(") *{} = {{", ret_interface)!;
		yield strings::concat("*", ret_interface);
	};
	defer free(rettype);

	fmt::printfln("\tlet buf = bufio::dynamic(io::mode::RDWR);")!;
	let inner = strio::dynamic();
	defer io::close(&inner)!;

	if (ret_interface != "") {
		if (ret_interface != "wayland::object") {
			fmt::printfln("\tlet iface = &{}_interface;", ret_interface)!;
			fmt::fprintf(&inner, "\n\tlet iface = &{}_interface;", ret_interface)!;
		};
		fmt::printfln("\tlet robj = iface.init(obj.object.client): *{};", ret_interface)!;
		fmt::printfln("\twayland::registry_add(&obj.object.client.registry, robj);")!;
		fmt::fprintf(&inner, "\n\tlet robj = iface.init(obj.object.client): *{};", ret_interface)!;
		fmt::fprintf(&inner, "\n\twayland::registry_add(&obj.object.client.registry, robj);")!;
	};

	for (let i = 0z; i < len(req.args); i += 1) {
		if (req.args[i].type_ != wayland::scanner::argtype::NEW_ID) {
			let argt = scanner::argtype_to_type(req.args[i].type_);
			fmt::printfln("\twayland::marshal_{}(obj.object.client, &buf, {}_);", argt, req.args[i].name)!;
			fmt::fprintf(&inner, "\n\twayland::marshal_{}(obj.object.client, &buf, {}_);", argt, req.args[i].name)!;
		} else {
			fmt::printfln("\twayland::marshal_u32(obj.object.client, &buf, (robj: *wayland::object).id);")!;
			fmt::fprintf(&inner, "\n\twayland::marshal_u32(obj.object.client, &buf, (robj: *wayland::object).id);")!;
			if (req.args[i].interface == "") {
				fmt::printfln("\twayland::marshal_u32(obj.object.client, &buf, version);")!;
				fmt::fprintf(&inner, "\n\twayland::marshal_u32(obj.object.client, &buf, version);")!;
			};
		};
	};

	fmt::printfln("\tmatch (wayland::queue_request(obj, {}, bufio::buffer(&buf))) {{", opcode)!;
	fmt::printfln("\tcase void => void;")!;
	fmt::printfln("\tcase let e: io::error => fmt::fatal(\"I/O error: {{}}\", io::strerror(e));")!;
	fmt::printfln("\t}};")!;
	if (ret_interface != "") {
		fmt::printfln("\treturn robj;")!;
	const return_statement = if (ret_interface != "") {
		yield "\treturn robj;\n";
	} else {
		yield "";
	};
	fmt::printfln("}};")!;

	template::execute(&request_function_template, os::stdout,
		("ifname", ifname(iface)),
		("name", req.name),
		("opcode", opcode),
		("fnargs", strio::string(&fnargs)),
		("inner", strio::string(&inner)),
		("rettype", rettype),
		("return", return_statement),
	)!;
};

fn event_callback(iface: *scanner::interface, ev: *scanner::event) void = {
	fmt::printfln("")!;
	let argscb = strio::dynamic();
	defer io::close(&argscb)!;

	fmt::printf("export type {}_{}_callback = *fn(userdata: nullable *void, obj: *{}", ifname(iface), ev.name, ifname(iface))!;
	for (let i = 0z; i < len(ev.args); i += 1) {
		fmt::printf(", ")!;
		fmt::printf("{}_: {}", ev.args[i].name, scanner::argtype_to_type(ev.args[i].type_))!;
		fmt::fprintf(&argscb, ", {}_: {}", ev.args[i].name, scanner::argtype_to_type(ev.args[i].type_))!;
	};
	fmt::printfln(") void;")!;

	fmt::printfln("")!;
	fmt::printfln("export fn {}_{}_unmarshal(id: wayland::object_id, conn: *wayland::client) void = {{", ifname(iface), ev.name)!;
	fmt::printfln("\tlet obj = match (wayland::registry_get(&conn.registry, id)) {{")!;
	fmt::printfln("\tcase let o: *wayland::object =>")!;
	fmt::printfln("\t\tyield o: *{};", ifname(iface))!;
	fmt::printfln("\tcase null =>")!;
	fmt::printfln("\t\tfmt::fatal(\"callback for unregistered object id\");")!;
	fmt::printfln("\t}};")!;
	// rbuffer includes header, start bufio::fixed from arguments
	fmt::printfln("\tlet buf = bufio::fixed(conn.mbuf[8..], io::mode::READ);")!;

	let argsfn = strio::dynamic();
	defer io::close(&argsfn)!;

	for (let i = 0z; i < len(ev.args); i += 1) {
		let argt = scanner::argtype_to_type(ev.args[i].type_);
		fmt::printfln("\tlet ev_arg_{}: {} = wayland::unmarshal_{}(conn, &buf);", ev.args[i].name, argt, argt)!;
		fmt::fprintf(&argsfn, "\n\tlet ev_arg_{}: {} = wayland::unmarshal_{}(conn, &buf);", ev.args[i].name, argt, argt)!;
		if (ev.args[i].type_ == scanner::argtype::STRING) {
			fmt::printfln("\tdefer free(ev_arg_{});", ev.args[i].name)!;
			fmt::fprintf(&argsfn, "\n\tdefer free(ev_arg_{});", ev.args[i].name)!;
		};
	};
	fmt::printfln("\tfor (let i = 0z; i < len(obj.listeners); i += 1) {{")!;
	fmt::printfln("\t\tmatch (obj.listeners[i].{}) {{", ev.name)!;
	fmt::printfln("\t\tcase let cb: {}_{}_callback =>", ifname(iface), ev.name)!;
	fmt::printf("\t\t\tcb(obj.listeners[i].userdata, obj")!;

	let evargs = strio::dynamic();
	defer io::close(&evargs)!;

	for (let i = 0z; i < len(ev.args); i += 1) {
		fmt::printf(", ")!;
		fmt::printf("ev_arg_{}", ev.args[i].name)!;
		fmt::fprintf(&evargs, ", ev_arg_{}", ev.args[i].name)!;
	};
	fmt::printfln(");")!;
	fmt::printfln("\t\tcase void =>")!;
	fmt::printfln("\t\t\tcontinue;")!;
	fmt::printfln("\t\t}};")!;
	fmt::printfln("\t}};")!;
	fmt::printfln("}};")!;

	template::execute(&eventcallback_template, os::stdout,
		("ifname", ifname(iface)),
		("evname", ev.name),
		("argscb", strio::string(&argscb)),
		("argsfn", strio::string(&argsfn)),
		("evargs", strio::string(&evargs)),
	)!;
};

fn listener(iface: *scanner::interface) void = {
	fmt::printfln("")!;
	fmt::printfln("export type {}_listener = struct {{", ifname(iface))!;
	fmt::printfln("\tuserdata: nullable *void,")!;
	let events = strio::dynamic();
	defer io::close(&events)!;

	for (let i = 0z; i < len(iface.events); i += 1) {
		fmt::printfln("\t{}: (void | {}_{}_callback),", iface.events[i].name, ifname(iface), iface.events[i].name)!;
		fmt::fprintfln(&events, "\t{}: (void | {}_{}_callback),", iface.events[i].name, ifname(iface), iface.events[i].name)!;
	};
	fmt::printfln("}};")!;
	fmt::printfln("")!;
	fmt::printfln("export fn {}_add_listener(o: *{}, listener: {}_listener) void = {{", ifname(iface), ifname(iface), ifname(iface))!;
	fmt::printfln("\tlet copy: *{}_listener = alloc(listener);", ifname(iface))!;
	fmt::printfln("\tappend(o.listeners, copy);")!;
	fmt::printfln("}};")!;

	template::execute(&listener_template, os::stdout,
		("ifname", ifname(iface)),
		("events", strio::string(&events)),
	)!;
};
-- 
2.38.2
Details
Message ID
<CQ3R65YZWBXH.XGX37MK89EC5@taiga>
In-Reply-To
<20230128103003.23298-1-contact+sr.ht@hacktivis.me> (view parent)
DKIM signature
missing
Download raw message
Needs rebase?
Details
Message ID
<Y9T77hISWXV+43Dl@cloudsdale.the-delta.net.eu.org>
In-Reply-To
<CQ3R65YZWBXH.XGX37MK89EC5@taiga> (view parent)
DKIM signature
missing
Download raw message
[2023-01-28 11:31:38+0100] Drew DeVault:
>Needs rebase?

Isn't that done? It's parent commit is eb6e32a
Details
Message ID
<CQ3RF5Q9ZVOM.3FJYWQSHQXNDZ@taiga>
In-Reply-To
<Y9T77hISWXV+43Dl@cloudsdale.the-delta.net.eu.org> (view parent)
DKIM signature
missing
Download raw message
Did not apply cleanly when I tried to pull it in
Reply to thread Export thread (mbox)