diff --git a/epicardium/api/genapi.py b/epicardium/api/genapi.py
index 725020e55e0c4f54636da98ed62c6de46b269820..c83358b50311eabc6a2bfa06b794c9eaf7954cb4 100644
--- a/epicardium/api/genapi.py
+++ b/epicardium/api/genapi.py
@@ -1,8 +1,79 @@
 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,169 +91,123 @@ 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 = """\
+    # 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)
-        )
-
-        # Evaluate the preprocessor
-        source = subprocess.check_output(
-            ["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,
-        )
+        header=os.path.relpath(args.header)
+    )
 
-        args_matcher = re.compile(r"(?P<type>(?:const )?\w+(?:\*+|\s+))(?P<name>\w+),")
+    # Evaluate the preprocessor
+    source = subprocess.check_output(
+        ["gcc", "-E", "-"], input=api_src.encode()
+    ).decode()
 
-        # Open output files
-        f_client = cx.enter_context(open(args.client, "w"))
-        f_server = cx.enter_context(open(args.server, "w"))
+    declarations = parse_declarations(source)
+    fmt_header = {
+        "header": os.path.basename(args.header)
+    }
 
-        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;
+        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,
-                )
+        buffer = _api_call_start({id}, size);
+        /* TODO: Check if buffer is not NULL */
+
+"""
+            f_client.write(tmp.format(**decl))
+
+            for i, arg in enumerate(decl["args"]):
+                tmp = """\
+        *({type}*)(buffer + {offset}) = {name};
+"""
+                f_client.write(tmp.format(**arg))
+
+            if decl["return_type"] == "void":
+                # Don't return if return type is void
+                tmp = """\
+
+        _api_call_transact(buffer);
+}}
+"""
+                f_client.write(tmp.format(**decl))
             else:
-                print("""\
-    case {id}:
-        {cdecl}(""".format(id=api_id, ret=api_return, 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,
-            )
-
-            if api_return != "void":
-                print(
-                """
-    return *({ret}*)_api_call_transact(buffer);
+                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(
-                """
-    _api_call_transact(buffer);
+                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;
+"""
+            f_dispatcher.write(tmp.format(**decl))
+
+        tmp = """\
+        default:
+                /* TODO: Better error handling */
+                printf("Error: API function %x is unknown!!\\n", id);
+                break;
+        }}
 }}
-""".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)
+"""
+        f_dispatcher.write(tmp.format(**fmt_header))
+    # END: Generate Dispatcher }}}
 
 
 if __name__ == "__main__":