use ast;
use ast::{ptype};
use fmt;
use io;
use strio;
use tmpl = strings::template;
// Generates code for a server to implement the given interface.
export fn server(out: io::handle, doc: *ast::document) (void | io::error) = {
fmt::fprintln(out, "// This file was generated by ipcgen; do not modify by hand")!;
fmt::fprintln(out, "use errors;")!;
fmt::fprintln(out, "use helios;")!;
fmt::fprintln(out, "use rt;")!;
fmt::fprintln(out)!;
for (let i = 0z; i < len(doc.interfaces); i += 1) {
const iface = &doc.interfaces[i];
s_iface(out, doc, iface)?;
};
};
const s_iface_header_src: str =
`export type $iface_iface = struct {`;
let st_iface_header: tmpl::template = [];
const s_iface_method_src: str =
` $method: *fn_$iface_$method,`;
let st_iface_method: tmpl::template = [];
@init fn s_iface() void = {
st_iface_header = tmpl::compile(s_iface_header_src)!;
st_iface_method = tmpl::compile(s_iface_method_src)!;
};
fn s_iface(
out: io::handle,
doc: *ast::document,
iface: *ast::interface,
) (void | io::error) = {
const id: ast::ident = [iface.name];
const name = gen_name_upper(&id);
defer free(name);
let id: ast::ident = alloc(doc.namespace...);
append(id, iface.name);
defer free(id);
const hash = genhash(&id);
fmt::fprintfln(out, "def {}_ID: u32 = 0x{:X};\n", name, hash)!;
for (let i = 0z; i < len(iface.methods); i += 1) {
const meth = &iface.methods[i];
s_method_fntype(out, iface, meth)?;
};
fmt::fprintln(out)?;
tmpl::execute(&st_iface_header, out,
("iface", iface.name),
)?;
fmt::fprintln(out)?;
for (let i = 0z; i < len(iface.methods); i += 1) {
const meth = &iface.methods[i];
tmpl::execute(&st_iface_method, out,
("iface", iface.name),
("method", meth.name),
)?;
fmt::fprintln(out)?;
};
fmt::fprintln(out, "};\n")?;
iface_label(out, iface, name)?;
fmt::fprintln(out)!;
s_iface_object(out, iface)?;
fmt::fprintln(out)!;
s_iface_dispatch(out, iface)?;
fmt::fprintln(out)!;
};
const s_method_fntype_src: str =
`export type fn_$iface_$method = fn(object: *$object$params) $result;`;
let st_method_fntype: tmpl::template = [];
@init fn s_method_fntype() void = {
st_method_fntype= tmpl::compile(s_method_fntype_src)!;
};
fn s_method_fntype(
out: io::handle,
iface: *ast::interface,
meth: *ast::method,
) (void | io::error) = {
assert(len(meth.caps_in) == 0); // TODO
assert(len(meth.caps_out) == 0); // TODO
let params = strio::dynamic();
defer io::close(¶ms)!;
if (len(meth.params) != 0) {
fmt::fprint(¶ms, ", ")?;
};
for (let i = 0z; i < len(meth.params); i += 1) {
const param = &meth.params[i];
fmt::fprintf(¶ms, "{}: ", param.name)!;
ipc_type(¶ms, ¶m.param_type)!;
if (i + 1 < len(meth.params)) {
fmt::fprint(¶ms, ", ")!;
};
};
let result = strio::dynamic();
defer io::close(&result)!;
ipc_type(&result, &meth.result)!;
tmpl::execute(&st_method_fntype, out,
("method", meth.name),
("iface", iface.name),
("object", iface.name),
("params", strio::string(¶ms)),
("result", strio::string(&result)),
)?;
fmt::fprintln(out)?;
};
const s_iface_object_src: str = `export type $iface = struct {
_iface: *$iface_iface,
_endpoint: helios::cap,
};`;
let st_iface_object: tmpl::template = [];
@init fn s_iface_object() void = {
st_iface_object = tmpl::compile(s_iface_object_src)!;
};
fn s_iface_object(
out: io::handle,
iface: *ast::interface,
) (void | io::error) = {
tmpl::execute(&st_iface_object, out,
("iface", iface.name),
)?;
fmt::fprintln(out)!;
};
const s_iface_dispatch_header_src: str = `export fn $iface_dispatch(
object: *$iface,
) void = {
const (tag, a1) = helios::recvraw(object._endpoint);
switch (rt::label(tag): $iface_label) {
`;
const s_iface_dispatch_footer_src: str = ` case =>
abort(); // TODO
};
};
`;
// XXX: It might be nice if we didn't have to do a reply syscall to detect that
// the user stored the reply capability for later
const s_iface_dispatch_method_src: str = ` case $iface_label::$methodid =>
${rval_store}object._iface.$method(
object,$params
);
match (helios::reply(0${rval_param})) {
case void =>
yield;
case errors::invalid_cslot =>
yield; // callee stored the reply
case errors::error =>
abort(); // TODO
};
`;
let st_iface_dispatch_header: tmpl::template = [];
let st_iface_dispatch_footer: tmpl::template = [];
let st_iface_dispatch_method: tmpl::template = [];
@init fn s_iface_dispatch() void = {
st_iface_dispatch_header = tmpl::compile(s_iface_dispatch_header_src)!;
st_iface_dispatch_footer = tmpl::compile(s_iface_dispatch_footer_src)!;
st_iface_dispatch_method = tmpl::compile(s_iface_dispatch_method_src)!;
};
fn s_iface_dispatch(
out: io::handle,
iface: *ast::interface,
) (void | io::error) = {
tmpl::execute(&st_iface_dispatch_header, out,
("iface", iface.name),
)?;
for (let serial = 0z; serial < len(iface.methods); serial += 1) {
const meth = &iface.methods[serial];
s_method_dispatch(out, iface, meth)?;
};
tmpl::execute(&st_iface_dispatch_footer, out)?;
};
fn s_method_dispatch(
out: io::handle,
iface: *ast::interface,
meth: *ast::method,
) (void | io::error) = {
const id: ast::ident = [meth.name];
const method_id = gen_name_upper(&id);
defer free(method_id);
let params = strio::dynamic();
defer io::close(¶ms)!;
for (let i = 0z; i < len(meth.params); i += 1) {
fmt::fprint(¶ms, "\n\t\t\t")!;
const param = &meth.params[i];
if (i == 0) {
fmt::fprint(¶ms, "a1")!;
} else {
fmt::fprintf(¶ms, "rt::ipcbuf.params[{}]", i)!;
};
fmt::fprint(¶ms, ": ")!;
ipc_type(¶ms, ¶m.param_type)!;
fmt::fprint(¶ms, ",")!;
};
let rval_store = strio::dynamic();
defer io::close(&rval_store)!;
let rval_param = strio::dynamic();
defer io::close(&rval_param)!;
if (!(meth.result is ptype) || meth.result as ptype != ptype::VOID) {
fmt::fprintf(&rval_store, "const rval = ")!;
fmt::fprintf(&rval_param, ", rval")!;
};
tmpl::execute(&st_iface_dispatch_method, out,
("iface", iface.name),
("method", meth.name),
("methodid", method_id),
("params", strio::string(¶ms)),
("rval_store", strio::string(&rval_store)),
("rval_param", strio::string(&rval_param)),
)?;
};