Commit 9d94f87e authored by ch3's avatar ch3

refact(genapi): Try to increase readability

parent cbb0f31e
...@@ -4,6 +4,155 @@ import os ...@@ -4,6 +4,155 @@ 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}"
"""
"""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,
)
# 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 = api_src_fmt.format(
header=os.path.relpath(header)
)
# Evaluate the preprocessor
source = subprocess.check_output(
["gcc", "-E", "-"], input=api_src.encode()
).decode()
return matcher.finditer(source)
def include_header (header:str, file):
print('#include "{}"\n'.format(
os.path.basename(header)
), file=file)
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 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))
return (api_args_names, api_args_types, api_args_sizes)
def serialise_arg (ty:str, api_args_sizes:list, arg_idx:int, arg:str, file):
print(
serialise_arg_fmt.format(
type=ty,
offset=" + ".join(api_args_sizes[:arg_idx]) if arg_idx > 0 else "0",
arg=arg,
),
file=file,
)
def deserialise_arg (ty:str, api_args_sizes:list, arg_idx:int, file):
if arg_idx != 0:
print(",", file=file)
print(
deserialise_arg_fmt.format(
type=ty,
offset=" + ".join(api_args_sizes[:arg_idx]) if arg_idx > 0 else "0",
),
file=file,
end="",
)
def insert_switch_case_break (file):
print(insert_switch_case_break_str,
file=file,
)
def bother_dispatcher (api_id:str, file):
print(bother_dispatcher_fmt.format(
id=api_id
),
file=file,
)
def switch_add_default (api_id:str, file):
print(switch_add_default_fmt.format(
id=api_id,
), file=file)
def main(): def main():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
...@@ -20,148 +169,45 @@ def main(): ...@@ -20,148 +169,45 @@ def main():
) )
args = parser.parse_args() args = parser.parse_args()
with contextlib.ExitStack() as cx: # Open output files
# Run the preprocessor on the header file to get the API definitions. cx = contextlib.ExitStack()
# f_client = cx.enter_context(open(args.client, "w"))
# For this, we first need a source to include the header which contains f_server = cx.enter_context(open(args.server, "w"))
# 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)
)
# Evaluate the preprocessor include_header (args.header, f_client)
source = subprocess.check_output( include_header (args.header, f_server)
["gcc", "-E", "-"], input=api_src.encode()
).decode()
# Parse the header for API definitions print(api_dispatch_call_start, file=f_server)
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+),") 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")
# Open output files (api_args_names, api_args_types, api_args_sizes) = \
f_client = cx.enter_context(open(args.client, "w")) destructure_args (api_args)
f_server = cx.enter_context(open(args.server, "w"))
print('#include "{}"\n'.format( print(api_func_start_fmt.format(
os.path.basename(args.header) id=api_id,
), file=f_client) cdecl=api_decl,
cargs=api_args,
total_size=" + ".join(api_args_sizes),
),
file=f_client,
)
print("""\ print(dispatch_case_fmt.format(id=api_id, cdecl=api_decl),
#include "{}" file=f_server,
)
void __api_dispatch_call(uint32_t id, void*buffer) for i, (arg, ty) in enumerate(zip(api_args_names, api_args_types)):
{{ serialise_arg (ty, api_args_sizes, i, arg, f_client)
switch (id) {{""".format( deserialise_arg (ty, api_args_sizes, i, f_server)
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")
api_args_names = []
api_args_types = []
api_args_sizes = []
# Destructure args
for match in args_matcher.finditer(api_args + ","):
arg_type = match.group("type").strip()
arg_name = match.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;
buffer = api_call_start({id}, size); insert_switch_case_break (f_server)
/* TODO: Check if buffer is no NULL */ bother_dispatcher (api_id, f_client)
""".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)):
print(
""" *({type}*)(buffer + {offset}) = {arg};""".format(
type=ty,
offset=" + ".join(api_args_sizes[:i]) if i > 0 else "0",
arg=arg,
),
file=f_client,
)
if i != 0:
print(",", file=f_server)
print(
"""\
*({type}*)(buffer + {offset})""".format(
type=ty,
offset=" + ".join(api_args_sizes[:i]) if i > 0 else "0",
),
file=f_server,
end="",
)
print("""
);
break;""".format(
cdecl=api_decl,
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); switch_add_default (api_id, f_server)
}}
""".format(
id=api_id
),
file=f_client,
)
print("""\
default:
printf("Error: API function %x is unknown!!\\n", {id});
break;
}}
}}""".format(
id=api_id,
), file=f_server)
if __name__ == "__main__": 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