From 9d94f87e43c6f5676c947269a86a5abfde0972fc Mon Sep 17 00:00:00 2001 From: ch3 <ch3@mailbox.org> Date: Sun, 16 Jun 2019 02:47:01 +0200 Subject: [PATCH] refact(genapi): Try to increase readability --- hw-tests/api-demo/genapi.py | 312 +++++++++++++++++++++--------------- 1 file changed, 179 insertions(+), 133 deletions(-) diff --git a/hw-tests/api-demo/genapi.py b/hw-tests/api-demo/genapi.py index 40cf1099..5f8eb5bc 100644 --- a/hw-tests/api-demo/genapi.py +++ b/hw-tests/api-demo/genapi.py @@ -4,6 +4,155 @@ import os import re 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(): parser = argparse.ArgumentParser( @@ -20,148 +169,45 @@ def main(): ) 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) - ) + # Open output files + cx = contextlib.ExitStack() + f_client = cx.enter_context(open(args.client, "w")) + f_server = cx.enter_context(open(args.server, "w")) - # Evaluate the preprocessor - source = subprocess.check_output( - ["gcc", "-E", "-"], input=api_src.encode() - ).decode() + include_header (args.header, f_client) + include_header (args.header, f_server) - # 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, - ) + print(api_dispatch_call_start, file=f_server) - 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 - f_client = cx.enter_context(open(args.client, "w")) - f_server = cx.enter_context(open(args.server, "w")) + (api_args_names, api_args_types, api_args_sizes) = \ + destructure_args (api_args) - print('#include "{}"\n'.format( - os.path.basename(args.header) - ), file=f_client) + 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("""\ -#include "{}" + print(dispatch_case_fmt.format(id=api_id, cdecl=api_decl), + file=f_server, + ) -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") - - 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; + 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) - 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)): - 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"); + insert_switch_case_break (f_server) + bother_dispatcher (api_id, f_client) - api_call_bother_dispatcher(buffer); -}} -""".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) + switch_add_default (api_id, f_server) if __name__ == "__main__": -- GitLab