|
@@ -8,10 +8,20 @@ def is_api_decl(decl, prefix):
|
|
|
return decl['name'].startswith(prefix)
|
|
|
elif decl['kind'] == 'EnumDecl':
|
|
|
# an anonymous enum, check if the items start with the prefix
|
|
|
- return decl['inner'][0]['name'].lower().startswith(prefix)
|
|
|
+ first = get_first_non_comment(decl['inner'])
|
|
|
+ return first['name'].lower().startswith(prefix)
|
|
|
else:
|
|
|
return False
|
|
|
|
|
|
+def get_first_non_comment(items):
|
|
|
+ return next(i for i in items if i['kind'] != 'FullComment')
|
|
|
+
|
|
|
+def strip_comments(items):
|
|
|
+ return [i for i in items if i['kind'] != 'FullComment']
|
|
|
+
|
|
|
+def extract_comment(comment, source):
|
|
|
+ return source[comment['range']['begin']['offset']:comment['range']['end']['offset']]
|
|
|
+
|
|
|
def is_dep_decl(decl, dep_prefixes):
|
|
|
for prefix in dep_prefixes:
|
|
|
if is_api_decl(decl, prefix):
|
|
@@ -27,12 +37,16 @@ def dep_prefix(decl, dep_prefixes):
|
|
|
def filter_types(str):
|
|
|
return str.replace('_Bool', 'bool')
|
|
|
|
|
|
-def parse_struct(decl):
|
|
|
+def parse_struct(decl, source):
|
|
|
outp = {}
|
|
|
outp['kind'] = 'struct'
|
|
|
outp['name'] = decl['name']
|
|
|
outp['fields'] = []
|
|
|
for item_decl in decl['inner']:
|
|
|
+ if item_decl['kind'] == 'FullComment':
|
|
|
+ outp['comment'] = extract_comment(item_decl, source)
|
|
|
+ outp['comment_multiline'] = '\n' in outp['comment']
|
|
|
+ continue
|
|
|
if item_decl['kind'] != 'FieldDecl':
|
|
|
sys.exit(f"ERROR: Structs must only contain simple fields ({decl['name']})")
|
|
|
item = {}
|
|
@@ -42,7 +56,7 @@ def parse_struct(decl):
|
|
|
outp['fields'].append(item)
|
|
|
return outp
|
|
|
|
|
|
-def parse_enum(decl):
|
|
|
+def parse_enum(decl, source):
|
|
|
outp = {}
|
|
|
if 'name' in decl:
|
|
|
outp['kind'] = 'enum'
|
|
@@ -53,24 +67,31 @@ def parse_enum(decl):
|
|
|
needs_value = True
|
|
|
outp['items'] = []
|
|
|
for item_decl in decl['inner']:
|
|
|
+ if item_decl['kind'] == 'FullComment':
|
|
|
+ outp['comment'] = extract_comment(item_decl, source)
|
|
|
+ outp['comment_multiline'] = '\n' in outp['comment']
|
|
|
+ continue
|
|
|
if item_decl['kind'] == 'EnumConstantDecl':
|
|
|
item = {}
|
|
|
item['name'] = item_decl['name']
|
|
|
if 'inner' in item_decl:
|
|
|
- const_expr = item_decl['inner'][0]
|
|
|
- if const_expr['kind'] != 'ConstantExpr':
|
|
|
- sys.exit(f"ERROR: Enum values must be a ConstantExpr ({item_decl['name']}), is '{const_expr['kind']}'")
|
|
|
- if const_expr['valueCategory'] != 'rvalue' and const_expr['valueCategory'] != 'prvalue':
|
|
|
- sys.exit(f"ERROR: Enum value ConstantExpr must be 'rvalue' or 'prvalue' ({item_decl['name']}), is '{const_expr['valueCategory']}'")
|
|
|
- if not ((len(const_expr['inner']) == 1) and (const_expr['inner'][0]['kind'] == 'IntegerLiteral')):
|
|
|
- sys.exit(f"ERROR: Enum value ConstantExpr must have exactly one IntegerLiteral ({item_decl['name']})")
|
|
|
- item['value'] = const_expr['inner'][0]['value']
|
|
|
+ exprs = strip_comments(item_decl['inner'])
|
|
|
+ if len(exprs) > 0:
|
|
|
+ const_expr = exprs[0]
|
|
|
+ if const_expr['kind'] != 'ConstantExpr':
|
|
|
+ sys.exit(f"ERROR: Enum values must be a ConstantExpr ({item_decl['name']}), is '{const_expr['kind']}'")
|
|
|
+ if const_expr['valueCategory'] != 'rvalue' and const_expr['valueCategory'] != 'prvalue':
|
|
|
+ sys.exit(f"ERROR: Enum value ConstantExpr must be 'rvalue' or 'prvalue' ({item_decl['name']}), is '{const_expr['valueCategory']}'")
|
|
|
+ const_expr_inner = strip_comments(const_expr['inner'])
|
|
|
+ if not ((len(const_expr_inner) == 1) and (const_expr_inner[0]['kind'] == 'IntegerLiteral')):
|
|
|
+ sys.exit(f"ERROR: Enum value ConstantExpr must have exactly one IntegerLiteral ({item_decl['name']})")
|
|
|
+ item['value'] = const_expr_inner[0]['value']
|
|
|
if needs_value and 'value' not in item:
|
|
|
- sys.exit(f"ERROR: anonymous enum items require an explicit value")
|
|
|
+ sys.exit("ERROR: anonymous enum items require an explicit value")
|
|
|
outp['items'].append(item)
|
|
|
return outp
|
|
|
|
|
|
-def parse_func(decl):
|
|
|
+def parse_func(decl, source):
|
|
|
outp = {}
|
|
|
outp['kind'] = 'func'
|
|
|
outp['name'] = decl['name']
|
|
@@ -78,6 +99,10 @@ def parse_func(decl):
|
|
|
outp['params'] = []
|
|
|
if 'inner' in decl:
|
|
|
for param in decl['inner']:
|
|
|
+ if param['kind'] == 'FullComment':
|
|
|
+ outp['comment'] = extract_comment(param, source)
|
|
|
+ outp['comment_multiline'] = '\n' in outp['comment']
|
|
|
+ continue
|
|
|
if param['kind'] != 'ParmVarDecl':
|
|
|
print(f" >> warning: ignoring func {decl['name']} (unsupported parameter type)")
|
|
|
return None
|
|
@@ -87,38 +112,41 @@ def parse_func(decl):
|
|
|
outp['params'].append(outp_param)
|
|
|
return outp
|
|
|
|
|
|
-def parse_decl(decl):
|
|
|
+def parse_decl(decl, source):
|
|
|
kind = decl['kind']
|
|
|
if kind == 'RecordDecl':
|
|
|
- return parse_struct(decl)
|
|
|
+ return parse_struct(decl, source)
|
|
|
elif kind == 'EnumDecl':
|
|
|
- return parse_enum(decl)
|
|
|
+ return parse_enum(decl, source)
|
|
|
elif kind == 'FunctionDecl':
|
|
|
- return parse_func(decl)
|
|
|
+ return parse_func(decl, source)
|
|
|
else:
|
|
|
return None
|
|
|
|
|
|
-def clang(csrc_path):
|
|
|
- cmd = ['clang', '-Xclang', '-ast-dump=json', '-c' ]
|
|
|
- cmd.append(csrc_path)
|
|
|
+def clang(csrc_path, with_comments=False):
|
|
|
+ cmd = ['clang', '-Xclang', '-ast-dump=json', "-c", csrc_path]
|
|
|
+ if with_comments:
|
|
|
+ cmd.append('-fparse-all-comments')
|
|
|
return subprocess.check_output(cmd)
|
|
|
|
|
|
-def gen(header_path, source_path, module, main_prefix, dep_prefixes):
|
|
|
- ast = clang(source_path)
|
|
|
+def gen(header_path, source_path, module, main_prefix, dep_prefixes, with_comments=False):
|
|
|
+ ast = clang(source_path, with_comments=with_comments)
|
|
|
inp = json.loads(ast)
|
|
|
outp = {}
|
|
|
outp['module'] = module
|
|
|
outp['prefix'] = main_prefix
|
|
|
outp['dep_prefixes'] = dep_prefixes
|
|
|
outp['decls'] = []
|
|
|
- for decl in inp['inner']:
|
|
|
- is_dep = is_dep_decl(decl, dep_prefixes)
|
|
|
- if is_api_decl(decl, main_prefix) or is_dep:
|
|
|
- outp_decl = parse_decl(decl)
|
|
|
- if outp_decl is not None:
|
|
|
- outp_decl['is_dep'] = is_dep
|
|
|
- outp_decl['dep_prefix'] = dep_prefix(decl, dep_prefixes)
|
|
|
- outp['decls'].append(outp_decl)
|
|
|
+ with open(header_path, 'r') as f:
|
|
|
+ source = f.read()
|
|
|
+ for decl in inp['inner']:
|
|
|
+ is_dep = is_dep_decl(decl, dep_prefixes)
|
|
|
+ if is_api_decl(decl, main_prefix) or is_dep:
|
|
|
+ outp_decl = parse_decl(decl, source)
|
|
|
+ if outp_decl is not None:
|
|
|
+ outp_decl['is_dep'] = is_dep
|
|
|
+ outp_decl['dep_prefix'] = dep_prefix(decl, dep_prefixes)
|
|
|
+ outp['decls'].append(outp_decl)
|
|
|
with open(f'{module}.json', 'w') as f:
|
|
|
f.write(json.dumps(outp, indent=2));
|
|
|
return outp
|