Skip to content

Commit c1955e5

Browse files
author
Povilas Kanapickas
committed
Transform/DDG: implement redirect generation
1 parent 04d3f97 commit c1955e5

File tree

1 file changed

+165
-5
lines changed

1 file changed

+165
-5
lines changed

index2ddg.py

Lines changed: 165 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,26 @@
5959
# a member function or free function
6060
ITEM_TYPE_FUNCTION = 2
6161

62+
# a constructor
63+
ITEM_TYPE_CONSTRUCTOR = 3
64+
65+
# a constructor that is described in the same page as the class
66+
ITEM_TYPE_CONSTRUCTOR_INLINEMEM = 4
67+
68+
# a destructor
69+
ITEM_TYPE_DESTRUCTOR = 5
70+
71+
# a destructor that is described in the same page as the class
72+
ITEM_TYPE_DESTRUCTOR_INLINEMEM = 6
73+
6274
# a member function that is described in the same page as the class
63-
ITEM_TYPE_FUNCTION_INLINEMEM = 3
75+
ITEM_TYPE_FUNCTION_INLINEMEM = 7
6476

6577
# an enum
66-
ITEM_TYPE_ENUM = 4
78+
ITEM_TYPE_ENUM = 8
6779

6880
# a value of an enum
69-
ITEM_TYPE_ENUM_CONST = 5
81+
ITEM_TYPE_ENUM_CONST = 9
7082

7183
def get_item_type(el):
7284
if (el.tag == 'const' and el.getparent().tag == 'enum' and
@@ -77,6 +89,16 @@ def get_item_type(el):
7789
return ITEM_TYPE_FUNCTION_INLINEMEM
7890
else:
7991
return ITEM_TYPE_FUNCTION
92+
if el.tag == 'constructor':
93+
if el.get('link') == '.':
94+
return ITEM_TYPE_CONSTRUCTOR_INLINEMEM
95+
else:
96+
return ITEM_TYPE_CONSTRUCTOR
97+
if el.tag == 'destructor':
98+
if el.get('link') == '.':
99+
return ITEM_TYPE_DESTRUCTOR_INLINEMEM
100+
else:
101+
return ITEM_TYPE_DESTRUCTOR
80102
if el.tag == 'class':
81103
return ITEM_TYPE_CLASS
82104
if el.tag == 'enum':
@@ -219,6 +241,135 @@ def build_abstract(decls, desc):
219241

220242
return all_code + desc
221243

244+
''' Outputs additional redirects for an identifier.
245+
246+
Firstly, we replace '::' with spaces. Then we add two redirects: one with
247+
unchanged text and another with '_' replaced with spaces. We strip one
248+
level of namespace/class qualification and repeat the process with the
249+
remaining text.
250+
251+
For constructors and destructors, we strip the function name and apply the
252+
abovementioned algorithm, the only difference being that we append
253+
(or prepend) 'constructor' or 'destructor' to the title of the redirect
254+
respectively.
255+
256+
Each redirect has a 'priority', which is defined by the number of stripped
257+
namespace/class qualifications from the entry that produced the redirect.
258+
This is used to remove duplicate redirects. For each group of duplicate
259+
redirects, we find the redirect with the highest priority (i.e. lowest
260+
number of qualifications stripped) and remove all other redirects. If the
261+
number of highest-priority redirects is more than one, then we remove all
262+
redirects from the group altogether.
263+
264+
We don't add any redirects to specializations, overloads or operators.
265+
'''
266+
''' array of dict { 'title' -> redirect title,
267+
'target' -> redirect target,
268+
'priority' -> redirect priority as int
269+
}
270+
'''
271+
redirects = []
272+
273+
def build_redirects(item_ident, item_type):
274+
global redirects
275+
276+
for ch in [ '(', ')', '<', '>', 'operator' ]:
277+
if ch in item_ident:
278+
return
279+
280+
target = item_ident
281+
parts = item_ident.split('::')
282+
283+
# -----
284+
def do_parts(parts, prepend='', append=''):
285+
global redirects
286+
if prepend != '':
287+
prepend = prepend + ' '
288+
if append != '':
289+
append = ' ' + append
290+
291+
p = 0
292+
while p < len(parts):
293+
redir1 = prepend + ' '.join(parts[p:]) + append
294+
redir2 = prepend + ' '.join(x.replace('_',' ') for x in parts[p:]) + append
295+
296+
redir1 = redir1.replace(' ', ' ').replace(' ', ' ')
297+
redir2 = redir2.replace(' ', ' ').replace(' ', ' ')
298+
299+
redirects.append({'title' : redir1, 'target' : target,
300+
'priority' : p})
301+
if redir1 != redir2:
302+
redirects.append({'title' : redir2, 'target' : target,
303+
'priority' : p})
304+
p += 1
305+
# -----
306+
307+
if item_type in [ ITEM_TYPE_CLASS,
308+
ITEM_TYPE_FUNCTION,
309+
ITEM_TYPE_FUNCTION_INLINEMEM,
310+
ITEM_TYPE_ENUM,
311+
ITEM_TYPE_ENUM_CONST ]:
312+
do_parts(parts)
313+
314+
elif item_type in [ ITEM_TYPE_CONSTRUCTOR,
315+
ITEM_TYPE_CONSTRUCTOR_INLINEMEM ]:
316+
parts.pop()
317+
do_parts(parts, prepend='constructor')
318+
do_parts(parts, append='constructor')
319+
elif item_type in [ ITEM_TYPE_DESTRUCTOR,
320+
ITEM_TYPE_DESTRUCTOR_INLINEMEM ]:
321+
parts.pop()
322+
do_parts(parts, prepend='destructor')
323+
do_parts(parts, append='destructor')
324+
else:
325+
pass # should not be here
326+
327+
def output_redirects():
328+
global redirects
329+
330+
# convert to a convenient data structure
331+
# dict { title -> dict { priority -> list ( targets ) } }
332+
redir_map = {}
333+
334+
for r in redirects:
335+
title = r['title']
336+
target = r['target']
337+
priority = r['priority']
338+
339+
if title not in redir_map:
340+
redir_map[title] = {}
341+
if priority not in redir_map[title]:
342+
redir_map[title][priority] = []
343+
344+
redir_map[title][priority].append(target)
345+
346+
# get non-duplicate redirects
347+
ok_redirects = [] # list ( dict { 'title' : title, 'target' : target })
348+
349+
for title in redir_map:
350+
# priority decreases with increasing values
351+
highest_prio = min(redir_map[title])
352+
if len(redir_map[title][highest_prio]) == 1:
353+
# not duplicate
354+
target = redir_map[title][highest_prio][0]
355+
ok_redirects.append({ 'title' : title, 'target' : target })
356+
357+
# sort the redirects
358+
ok_redirects = sorted(ok_redirects, key=lambda x: x['title'])
359+
360+
# output
361+
for r in ok_redirects:
362+
# title
363+
line = r['title'] + '\t'
364+
# type
365+
line += 'R\t'
366+
# redirect
367+
line += r['target'] + '\t'
368+
# otheruses, categories, references, see_also, further_reading,
369+
# external links, disambiguation, images, abstract, source url
370+
line += '\t\t\t\t\t\t\t\t\t\t\n'
371+
out.write(line)
372+
222373
if debug:
223374
out = sys.stdout
224375
else:
@@ -258,12 +409,16 @@ def build_abstract(decls, desc):
258409
desc = get_short_description(root, get_version(decls), debug=debug_verbose)
259410
abstract = build_abstract(decls, desc)
260411

261-
elif item_type == ITEM_TYPE_FUNCTION:
412+
elif item_type in [ ITEM_TYPE_FUNCTION,
413+
ITEM_TYPE_CONSTRUCTOR,
414+
ITEM_TYPE_DESTRUCTOR ]:
262415
decls = get_declarations(root, name)
263416
desc = get_short_description(root, get_version(decls), debug=debug_verbose)
264417
abstract = build_abstract(decls, desc)
265418

266-
elif item_type == ITEM_TYPE_FUNCTION_INLINEMEM:
419+
elif item_type in [ ITEM_TYPE_FUNCTION_INLINEMEM,
420+
ITEM_TYPE_CONSTRUCTOR_INLINEMEM,
421+
ITEM_TYPE_DESTRUCTOR_INLINEMEM ]:
267422
raise DdgException("INLINEMEM") # not implemented
268423
''' Implementation notes:
269424
* the declarations are possibly versioned
@@ -301,11 +456,16 @@ def build_abstract(decls, desc):
301456
# source url
302457
line += 'http://en.cppreference.com/w/' + link + '\n'
303458
out.write(line)
459+
460+
build_redirects(item_ident, item_type)
461+
304462
except DdgException as err:
305463
if debug:
306464
line = '# error (' + str(err) + "): " + link + ": " + item_ident + "\n"
307465
out.write(line)
308466

467+
output_redirects()
468+
309469
if debug:
310470
print('=============================')
311471
print('Numbers of lines used:')

0 commit comments

Comments
 (0)