• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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