1# Copyright Hans Dembinski 2018 - 2019. 2# Distributed under the Boost Software License, Version 1.0. 3# (See accompanying file LICENSE_1_0.txt or copy at 4# https://www.boost.org/LICENSE_1_0.txt) 5 6import sys 7import xml.etree.ElementTree as ET 8import re 9 10 11def log(*args): 12 sys.stdout.write("post-processing:" + " ".join(args) + "\n") 13 14 15def select(condition, *tags): 16 result = [] 17 for tag in tags: 18 for item in root.iter(tag): 19 if condition(item): 20 result.append(item) 21 return result 22 23 24def is_detail(x): 25 if x.text is not None: 26 if "detail" in x.text: 27 return True 28 m = re.match("(?:typename)? *([A-Za-z0-9_\:]+)", x.text) 29 if m is not None: 30 s = m.group(1) 31 if s.startswith("detail") or s.endswith("_impl"): 32 x.text = s 33 return True 34 35 p = x.find("purpose") 36 if p is not None: 37 return p.text.lower().lstrip().startswith("implementation detail") 38 return False 39 40 41def is_deprecated(x): 42 p = x.find("purpose") 43 if p is not None: 44 return p.text.lower().lstrip().startswith("deprecated") 45 return False 46 47 48def sort_headers_by(x): 49 name = x.get("name") 50 return name.count("/"), name 51 52 53def tail_stripper(elem): 54 if elem.tail: 55 elem.tail = elem.tail.rstrip() 56 for item in elem: 57 tail_stripper(item) 58 59 60def item_sorter(elem): 61 if elem.tag == "namespace": 62 if len(elem) > 1: 63 items = list(elem) 64 items.sort(key=lambda x: x.get("name")) 65 elem[:] = items 66 for sub in elem: 67 item_sorter(sub) 68 69 70if "ipykernel" in sys.argv[0]: # used when run interactively in Atom/Hydrogen 71 from pathlib import Path 72 73 for input_file in Path().rglob("reference.xml"): 74 input_file = str(input_file) 75 break 76else: 77 input_file = sys.argv[1] 78 79output_file = input_file.replace(".xml", "_pp.xml") 80 81tree = ET.parse(input_file) 82root = tree.getroot() 83 84parent_map = {c: p for p in tree.iter() for c in p} 85 86unspecified = ET.Element("emphasis") 87unspecified.text = "unspecified" 88 89# - hide all unnamed template parameters, these are used for SFINAE 90# - hide all template parameters that start with Detail 91for item in select( 92 lambda x: x.get("name") == "" or x.get("name").startswith("Detail"), 93 "template-type-parameter", 94 "template-nontype-parameter", 95): 96 parent = parent_map[item] 97 assert parent.tag == "template" 98 parent.remove(item) 99 parent = parent_map[parent] 100 name = parent.get("name") 101 if name is None: 102 log("removing unnamed template parameter from", parent.tag) 103 else: 104 log("removing unnamed template parameter from", parent.tag, name) 105 106# replace any type with "detail" in its name with "unspecified" 107for item in select(is_detail, "type"): 108 log("replacing", '"%s"' % item.text, 'with "unspecified"') 109 item.clear() 110 item.append(unspecified) 111 112# hide everything that's deprecated 113for item in select(is_deprecated, "typedef"): 114 parent = parent_map[item] 115 log( 116 "removing deprecated", 117 item.tag, 118 item.get("name"), 119 "from", 120 parent.tag, 121 parent.get("name"), 122 ) 123 parent.remove(item) 124 125# hide private member functions 126for item in select( 127 lambda x: x.get("name") == "private member functions", "method-group" 128): 129 parent = parent_map[item] 130 log("removing private member functions from", parent.tag, parent.get("name")) 131 parent.remove(item) 132 133# hide undocumented classes, structs, functions and replace those declared 134# "implementation detail" with typedef to unspecified 135for item in select(lambda x: True, "class", "struct", "function"): 136 purpose = item.find("purpose") 137 if purpose is None: 138 parent = parent_map[item] 139 log( 140 "removing undocumented", 141 item.tag, 142 item.get("name"), 143 "from", 144 parent.tag, 145 parent.get("name"), 146 ) 147 if item in parent_map[item]: 148 parent_map[item].remove(item) 149 elif purpose.text.strip().lower() == "implementation detail": 150 log("replacing", item.tag, item.get("name"), "with unspecified typedef") 151 name = item.get("name") 152 item.clear() 153 item.tag = "typedef" 154 item.set("name", name) 155 type = ET.Element("type") 156 type.append(unspecified) 157 item.append(type) 158 159parent_map = {c: p for p in tree.iter() for c in p} 160 161# hide methods and constructors explicitly declared as "implementation detail" 162for item in select(is_detail, "constructor", "method"): 163 name = item.get("name") 164 log( 165 "removing", 166 (item.tag + " " + name) if name is not None else item.tag, 167 "declared as implementation detail", 168 ) 169 parent_map[item].remove(item) 170 171 172log("sorting headers") 173reference = tree.getroot() 174assert reference.tag == "library-reference" 175reference[:] = sorted(reference, key=sort_headers_by) 176 177 178log("sorting items in each namespace") 179for header in reference: 180 namespace = header.find("namespace") 181 if namespace is None: 182 continue 183 item_sorter(namespace) 184 185 186log("stripping trailing whitespace") 187tail_stripper(reference) 188 189tree.write(output_file) 190