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

refact(genapi): Try to increase readability

parent cbb0f31e
...@@ -4,33 +4,76 @@ import os ...@@ -4,33 +4,76 @@ import os
import re import re
import subprocess import subprocess
api_src_fmt = """\
#define API(id, def) __GENERATE_API $ __GEN_ID_##id $ def $
#include "{header}"
"""
def main():
parser = argparse.ArgumentParser( """Generated Client file"""
description="Generate the API stubs from a header file." api_func_start_fmt = """\
) /* Autogenerated stub for {id} */
parser.add_argument( void {cdecl}({cargs})
"-H", "--header", required=True, help="The header to base the definitions on." {{
) const int size = {total_size};
parser.add_argument( void*buffer;
"-c", "--client", required=True, help="The output client-side c source file."
) buffer = api_call_start({id}, size);
parser.add_argument( /* TODO: Check if buffer is no NULL */
"-s", "--server", required=True, help="The output server-side c source file." """
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. # 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 # For this, we first need a source to include the header which contains
# an alternative definition of the `API` macro that marks definitions in # an alternative definition of the `API` macro that marks definitions in
# a way we can find later on. # a way we can find later on.
api_src = """\ api_src = api_src_fmt.format(
#define API(id, def) __GENERATE_API $ __GEN_ID_##id $ def $ header=os.path.relpath(header)
#include "{header}"
""".format(
header=os.path.relpath(args.header)
) )
# Evaluate the preprocessor # Evaluate the preprocessor
...@@ -38,130 +81,133 @@ def main(): ...@@ -38,130 +81,133 @@ def main():
["gcc", "-E", "-"], input=api_src.encode() ["gcc", "-E", "-"], input=api_src.encode()
).decode() ).decode()
# Parse the header for API definitions return matcher.finditer(source)
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+),")
# 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( print('#include "{}"\n'.format(
os.path.basename(args.header) os.path.basename(header)
), file=f_client) ), file=file)
print("""\
#include "{}"
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_names = []
api_args_types = [] api_args_types = []
api_args_sizes = [] api_args_sizes = []
args_matcher = re.compile(r"(?P<type>\w+(?:\*+|\s+))(?P<name>\w+),")
# Destructure args # Destructure args
for match in args_matcher.finditer(api_args + ","): for arg in args_matcher.finditer(api_args + ","):
arg_type = match.group("type").strip() arg_type = arg.group("type").strip()
arg_name = match.group("name") arg_name = arg.group("name")
api_args_names.append(arg_name) api_args_names.append(arg_name)
api_args_types.append(arg_type) api_args_types.append(arg_type)
api_args_sizes.append("sizeof({})".format(arg_type)) api_args_sizes.append("sizeof({})".format(arg_type))
print( return (api_args_names, api_args_types, api_args_sizes)
"""\
/* 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 */
""".format(
id=api_id,
cdecl=api_decl,
cargs=api_args,
total_size=" + ".join(api_args_sizes),
),
file=f_client,
)
print("""\ def serialise_arg (ty:str, api_args_sizes:list, arg_idx:int, arg:str, file):
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)):
print( print(
""" *({type}*)(buffer + {offset}) = {arg};""".format( serialise_arg_fmt.format(
type=ty, 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, arg=arg,
), ),
file=f_client, file=file,
) )
if i != 0: def deserialise_arg (ty:str, api_args_sizes:list, arg_idx:int, file):
print(",", file=f_server) if arg_idx != 0:
print(",", file=file)
print( print(
"""\ deserialise_arg_fmt.format(
*({type}*)(buffer + {offset})""".format(
type=ty, 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="", end="",
) )
print("""
); def insert_switch_case_break (file):
break;""".format( print(insert_switch_case_break_str,
cdecl=api_decl, file=file,
args=", ".join(api_args_names),
),
file=f_server,
) )
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); def bother_dispatcher (api_id:str, file):
}} print(bother_dispatcher_fmt.format(
""".format(
id=api_id id=api_id
), ),
file=f_client, file=file,
) )
print("""\
default: def switch_add_default (api_id:str, file):
printf("Error: API function %x is unknown!!\\n", {id}); print(switch_add_default_fmt.format(
break;
}}
}}""".format(
id=api_id, 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__": if __name__ == "__main__":
......
Supports Markdown
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