Haelwenn (lanodan) Monnier: 2 Makefile: Add `check` target cmd/scanner: Use strings::template and merge interface+request+event 2 files changed, 276 insertions(+), 145 deletions(-)
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~sircmpwn/public-inbox/patches/37165/mbox | git am -3Learn more about email & git
From: "Haelwenn (lanodan) Monnier" <contact@hacktivis.me> --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index e86174d..629caeb 100644 --- a/Makefile +++ b/Makefile @@ -19,4 +19,7 @@ generate: wayland/wl/protocol.ha hare-wlscanner: cmd/scanner/main.ha hare build $(HAREFLAGS) -o $@ $< -.PHONY: all clean generate hare-wlscanner +check: + hare test + +.PHONY: all clean generate hare-wlscanner check -- 2.37.4
Thanks! To git@git.sr.ht:~sircmpwn/hare-wayland fee39ab..3bc7100 master -> master
From: "Haelwenn (lanodan) Monnier" <contact@hacktivis.me> --- cmd/scanner/main.ha | 416 +++++++++++++++++++++++++++++--------------- 1 file changed, 272 insertions(+), 144 deletions(-) diff --git a/cmd/scanner/main.ha b/cmd/scanner/main.ha index 698aed6..103776e 100644 --- a/cmd/scanner/main.ha +++ b/cmd/scanner/main.ha @@ -1,9 +1,162 @@ 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"); + }; + let buf = bufio::fixed(conn.rbuffer, 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|void) = void; +let interface_template: (template::template|void) = void; +let arg_template: (template::template|void) = void; +let request_template: (template::template|void) = void; +let event_template: (template::template|void) = void; +let request_function_template: (template::template|void) = void; +let eventcallback_template: (template::template|void) = void; +let listener_template: (template::template|void) = void; + +@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 as template::template)); + template::finish(&(interface_template as template::template)); + template::finish(&(arg_template as template::template)); + template::finish(&(request_template as template::template)); + template::finish(&(event_template as template::template)); + template::finish(&(request_function_template as template::template)); + template::finish(&(eventcallback_template as template::template)); + template::finish(&(listener_template as template::template)); +}; + @noreturn fn usage() void = fmt::fatalf("Usage: {} <client|server> < input.xml > output.ha", os::args[0]); @@ -22,11 +175,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 as template::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 +194,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 as template::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 as template::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 as template::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 as template::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 as template::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,112 +277,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 as template::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}};")!; - fmt::printfln("\tlet buf = bufio::fixed(conn.rbuffer, 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 as template::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 as template::template), os::stdout, + ("ifname", ifname(iface)), + ("events", strio::string(&events)), + )!; }; -- 2.37.4