Commit 9d94f87e authored by ch3's avatar ch3
Browse files

refact(genapi): Try to increase readability

parent cbb0f31e
......@@ -4,33 +4,76 @@ import os
import re
import subprocess
api_src_fmt = """\
#define API(id, def) __GENERATE_API $ __GEN_ID_##id $ def $
#include "{header}"
"""
def main():
parser = argparse.ArgumentParser(
description="Generate the API stubs from a header file."
)
parser.add_argument(
"-H", "--header", required=True, help="The header to base the definitions on."
)
parser.add_argument(
"-c", "--client", required=True, help="The output client-side c source file."
)
parser.add_argument(
"-s", "--server", required=True, help="The output server-side c source file."
"""Generated Client file"""
api_func_start_fmt = """\
/* Autogenerated stub for {id} */
void {cdecl}({cargs})
{{
const int size = {total_size};
void*buffer;
buffer = api_call_start({id}, size);
/* TODO: Check if buffer is no NULL */
"""
serialise_arg_fmt = """ *({type}*)(buffer + {offset}) = {arg};"""
bother_dispatcher_fmt = """
printf("Sending call {id}\\nBUF: ");
for (int i = 0; i < size; i++) {{
printf("0x%02x ", ((char*)buffer)[i]);
}}
printf("\\n");
api_call_bother_dispatcher(buffer);
}}
"""
"""Generated Service file"""
api_dispatch_call_start = """\
void __api_dispatch_call(uint32_t id, void*buffer)
{
switch (id) {"""
dispatch_case_fmt = """\
case {id}:
{cdecl}("""
deserialise_arg_fmt = """\
*({type}*)(buffer + {offset})"""
insert_switch_case_break_str = """
);
break;"""
switch_add_default_fmt = """\
default:
printf("Error: API function %x is unknown!!\\n", {id});
break;
}}
}}"""
def api_func_iter (header:str):
# Parse the header for API definitions
matcher = re.compile(
r"__GENERATE_API \$ __GEN_ID_(?P<id>\w+) \$ void (?P<decl>.+?)\((?P<args>.*?)\) \$",
re.DOTALL | re.MULTILINE,
)
args = parser.parse_args()
with contextlib.ExitStack() as cx:
# Run the preprocessor on the header file to get the API definitions.
#
# For this, we first need a source to include the header which contains
# an alternative definition of the `API` macro that marks definitions in
# a way we can find later on.
api_src = """\
#define API(id, def) __GENERATE_API $ __GEN_ID_##id $ def $
#include "{header}"
""".format(
header=os.path.relpath(args.header)
api_src = api_src_fmt.format(
header=os.path.relpath(header)
)
# Evaluate the preprocessor
......@@ -38,130 +81,133 @@ def main():
["gcc", "-E", "-"], input=api_src.encode()
).decode()
# Parse the header for API definitions
matcher = re.compile(
r"__GENERATE_API \$ __GEN_ID_(?P<id>\w+) \$ void (?P<decl>.+?)\((?P<args>.*?)\) \$",
re.DOTALL | re.MULTILINE,
)
args_matcher = re.compile(r"(?P<type>\w+(?:\*+|\s+))(?P<name>\w+),")
return matcher.finditer(source)
# Open output files
f_client = cx.enter_context(open(args.client, "w"))
f_server = cx.enter_context(open(args.server, "w"))
def include_header (header:str, file):
print('#include "{}"\n'.format(
os.path.basename(args.header)
), file=f_client)
print("""\
#include "{}"
os.path.basename(header)
), file=file)
void __api_dispatch_call(uint32_t id, void*buffer)
{{
switch (id) {{""".format(
os.path.basename(args.header)
), file=f_server)
for match in matcher.finditer(source):
api_id = match.group("id")
api_decl = match.group("decl")
api_args = match.group("args")
def destructure_args (api_args:str):
api_args_names = []
api_args_types = []
api_args_sizes = []
args_matcher = re.compile(r"(?P<type>\w+(?:\*+|\s+))(?P<name>\w+),")
# Destructure args
for match in args_matcher.finditer(api_args + ","):
arg_type = match.group("type").strip()
arg_name = match.group("name")
for arg in args_matcher.finditer(api_args + ","):
arg_type = arg.group("type").strip()
arg_name = arg.group("name")
api_args_names.append(arg_name)
api_args_types.append(arg_type)
api_args_sizes.append("sizeof({})".format(arg_type))
print(
"""\
/* Autogenerated stub for {id} */
void {cdecl}({cargs})
{{
const int size = {total_size};
void*buffer;
return (api_args_names, api_args_types, api_args_sizes)
buffer = api_call_start({id}, size);
/* TODO: Check if buffer is no NULL */
""".format(
id=api_id,
cdecl=api_decl,
cargs=api_args,
total_size=" + ".join(api_args_sizes),
),
file=f_client,
)
print("""\
case {id}:
{cdecl}(""".format(id=api_id, cdecl=api_decl),
file=f_server,
)
for i, (arg, ty) in enumerate(zip(api_args_names, api_args_types)):
def serialise_arg (ty:str, api_args_sizes:list, arg_idx:int, arg:str, file):
print(
""" *({type}*)(buffer + {offset}) = {arg};""".format(
serialise_arg_fmt.format(
type=ty,
offset=" + ".join(api_args_sizes[:i]) if i > 0 else "0",
offset=" + ".join(api_args_sizes[:arg_idx]) if arg_idx > 0 else "0",
arg=arg,
),
file=f_client,
file=file,
)
if i != 0:
print(",", file=f_server)
def deserialise_arg (ty:str, api_args_sizes:list, arg_idx:int, file):
if arg_idx != 0:
print(",", file=file)
print(
"""\
*({type}*)(buffer + {offset})""".format(
deserialise_arg_fmt.format(
type=ty,
offset=" + ".join(api_args_sizes[:i]) if i > 0 else "0",
offset=" + ".join(api_args_sizes[:arg_idx]) if arg_idx > 0 else "0",
),
file=f_server,
file=file,
end="",
)
print("""
);
break;""".format(
cdecl=api_decl,
args=", ".join(api_args_names),
),
file=f_server,
def insert_switch_case_break (file):
print(insert_switch_case_break_str,
file=file,
)
print(
"""
printf("Sending call {id}\\nBUF: ");
for (int i = 0; i < size; i++) {{
printf("0x%02x ", ((char*)buffer)[i]);
}}
printf("\\n");
api_call_bother_dispatcher(buffer);
}}
""".format(
def bother_dispatcher (api_id:str, file):
print(bother_dispatcher_fmt.format(
id=api_id
),
file=f_client,
file=file,
)
print("""\
default:
printf("Error: API function %x is unknown!!\\n", {id});
break;
}}
}}""".format(
def switch_add_default (api_id:str, file):
print(switch_add_default_fmt.format(
id=api_id,
), file=f_server)
), file=file)
def main():
parser = argparse.ArgumentParser(
description="Generate the API stubs from a header file."
)
parser.add_argument(
"-H", "--header", required=True, help="The header to base the definitions on."
)
parser.add_argument(
"-c", "--client", required=True, help="The output client-side c source file."
)
parser.add_argument(
"-s", "--server", required=True, help="The output server-side c source file."
)
args = parser.parse_args()
# Open output files
cx = contextlib.ExitStack()
f_client = cx.enter_context(open(args.client, "w"))
f_server = cx.enter_context(open(args.server, "w"))
include_header (args.header, f_client)
include_header (args.header, f_server)
print(api_dispatch_call_start, file=f_server)
for api_func in api_func_iter (args.header):
api_id = api_func.group("id")
api_decl = api_func.group("decl")
api_args = api_func.group("args")
(api_args_names, api_args_types, api_args_sizes) = \
destructure_args (api_args)
print(api_func_start_fmt.format(
id=api_id,
cdecl=api_decl,
cargs=api_args,
total_size=" + ".join(api_args_sizes),
),
file=f_client,
)
print(dispatch_case_fmt.format(id=api_id, cdecl=api_decl),
file=f_server,
)
for i, (arg, ty) in enumerate(zip(api_args_names, api_args_types)):
serialise_arg (ty, api_args_sizes, i, arg, f_client)
deserialise_arg (ty, api_args_sizes, i, f_server)
insert_switch_case_break (f_server)
bother_dispatcher (api_id, f_client)
switch_add_default (api_id, f_server)
if __name__ == "__main__":
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment