Skip to content
Snippets Groups Projects
Commit f49d850f authored by rahix's avatar rahix
Browse files

Merge branch 'rahix/genapi-v2' into 'master'

Refactor genapi.py script

See merge request !9
parents 15bb6ac6 b38163f2
Branches
Tags
No related merge requests found
import argparse
import contextlib
import os
import re
import subprocess
import sys
MATCH_EXPANSION = re.compile(
r"__GENERATE_API \$ __GEN_ID_(?P<id>\w+) \$ (?P<decl>.*?) \$",
re.DOTALL | re.MULTILINE,
)
MATCH_DECLARATION = re.compile(
r"^(?P<typename>.*?)\s*\((?P<args>.*)\)$",
re.DOTALL,
)
MATCH_TYPENAME = re.compile(
r"^(?P<type>(?:const )?(?:struct )?\w+(?:\s+|\*+))(?P<name>\w+)$",
)
def sizeof(args):
"""Return a string that describes the size of a list of arguments."""
return " + ".join(a["sizeof"] for a in args) if args != [] else "0"
def bailout(message, *args, **kwargs):
fmt = "\x1B[31;1mError\x1B[0m: {}"
print(fmt.format(message.format(*args, **kwargs)), file=sys.stderr)
sys.exit(1)
def parse_declarations(source):
"""Parse all declarations in the given source."""
declarations = []
for exp in MATCH_EXPANSION.finditer(source):
id = exp.group("id")
decl = MATCH_DECLARATION.match(exp.group("decl"))
if decl is None:
bailout("Error in declaration '{}'", exp.group("decl"))
typename = MATCH_TYPENAME.match(decl.group("typename"))
args = []
args_str = decl.group("args")
if typename is None:
bailout("Error in declaration '{}'", exp.group("decl"))
# Parse arguments
for arg_str in map(str.strip, args_str.split(",")):
if arg_str in ["void", ""]:
continue
arg = MATCH_TYPENAME.match(arg_str.strip())
if arg is None:
bailout("Failed to parse argument '{}'", arg_str.strip())
args.append({
"type": arg.group("type").strip(),
"name": arg.group("name"),
"sizeof": "sizeof({})".format(arg.group("type").strip()),
"offset": sizeof(args),
})
declarations.append({
"id": id,
"return_type": typename.group("type").strip(),
"name": typename.group("name"),
"args": args,
"args_str": args_str,
})
return declarations
def main():
......@@ -20,7 +91,6 @@ 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
......@@ -38,151 +108,106 @@ 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+) \$ (?P<type>\w+(?:\*+|\s+))(?P<decl>.+?)\((?P<args>.*?)\) \$",
re.DOTALL | re.MULTILINE,
)
args_matcher = re.compile(r"(?P<type>(?:const )?\w+(?:\*+|\s+))(?P<name>\w+),")
declarations = parse_declarations(source)
fmt_header = {
"header": os.path.basename(args.header)
}
# Open output files
f_client = cx.enter_context(open(args.client, "w"))
f_server = cx.enter_context(open(args.server, "w"))
print("""\
# Generate Client {{{
with open(args.client, "w") as f_client:
tmp = """\
#include <stdio.h>
#include "{}"
#include "{header}"
#include "api/caller.h"
""".format(
os.path.basename(args.header)
), file=f_client)
"""
f_client.write(tmp.format(**fmt_header))
print("""\
#include <stdio.h>
#include "{}"
for decl in declarations:
decl["total_size"] = sizeof(decl["args"])
tmp = """\
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_return = match.group("type").strip()
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} */
{ret} {cdecl}({cargs})
{return_type} {name}({args_str})
{{
const int size = {total_size};
void*buffer;
buffer = _api_call_start({id}, size);
/* TODO: Check if buffer is no NULL */
""".format(
id=api_id,
ret=api_return,
cdecl=api_decl,
cargs=api_args,
total_size=" + ".join(api_args_sizes) if api_args_sizes != [] else "0",
),
file=f_client,
)
if api_return != "void":
print("""\
case {id}:
*(({ret}*)buffer) = {cdecl}(""".format(id=api_id, ret=api_return, cdecl=api_decl),
file=f_server,
)
else:
print("""\
case {id}:
{cdecl}(""".format(id=api_id, ret=api_return, cdecl=api_decl),
file=f_server,
)
/* TODO: Check if buffer is not NULL */
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,
)
"""
f_client.write(tmp.format(**decl))
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="",
)
for i, arg in enumerate(decl["args"]):
tmp = """\
*({type}*)(buffer + {offset}) = {name};
"""
f_client.write(tmp.format(**arg))
print("""
);
break;""".format(
cdecl=api_decl,
args=", ".join(api_args_names),
),
file=f_server,
)
if decl["return_type"] == "void":
# Don't return if return type is void
tmp = """\
if api_return != "void":
print(
_api_call_transact(buffer);
}}
"""
return *({ret}*)_api_call_transact(buffer);
f_client.write(tmp.format(**decl))
else:
tmp = """\
return *({return_type}*)_api_call_transact(buffer);
}}
""".format(
id=api_id,
ret=api_return,
),
file=f_client,
)
"""
f_client.write(tmp.format(**decl))
# END: Generate Client }}}
# Generate Dispatcher {{{
with open(args.server, "w") as f_dispatcher:
tmp = """\
#include <stdio.h>
#include "{header}"
void __api_dispatch_call(uint32_t id, void*buffer)
{{
switch (id) {{
"""
f_dispatcher.write(tmp.format(**fmt_header))
for decl in declarations:
if decl["return_type"] == "void":
tmp = """\
case {id}:
{name}("""
f_dispatcher.write(tmp.format(**decl))
else:
print(
tmp = """\
case {id}:
*(({return_type}*)buffer) = {name}("""
f_dispatcher.write(tmp.format(**decl))
for i, arg in enumerate(decl["args"]):
arg["comma"] = "" if i == 0 else ","
tmp = """{comma}
*({type}*)(buffer + {offset})"""
f_dispatcher.write(tmp.format(**arg))
tmp = """
);
break;
"""
_api_call_transact(buffer);
}}
""".format(
id=api_id,
),
file=f_client,
)
f_dispatcher.write(tmp.format(**decl))
print("""\
tmp = """\
default:
printf("Error: API function %x is unknown!!\\n", {id});
/* TODO: Better error handling */
printf("Error: API function %x is unknown!!\\n", id);
break;
}}
}}""".format(
id=api_id,
), file=f_server)
}}
"""
f_dispatcher.write(tmp.format(**fmt_header))
# END: Generate Dispatcher }}}
if __name__ == "__main__":
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment