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 7ba6d90..5dba0f9 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");
+ };
+ // 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 +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, os::stdout, ("name", proto.name))!;
+
for (let i = 0z; i < len(proto.interfaces); i += 1) {
interface(&proto.interfaces[i]);
functions(&proto.interfaces[i]);
@@ -44,92 +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::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 = {
@@ -150,113 +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, 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