]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - docs/tools/dump_format_style.py
Vendor import of clang trunk r338150:
[FreeBSD/FreeBSD.git] / docs / tools / dump_format_style.py
1 #!/usr/bin/env python
2 # A tool to parse the FormatStyle struct from Format.h and update the
3 # documentation in ../ClangFormatStyleOptions.rst automatically.
4 # Run from the directory in which this file is located to update the docs.
5
6 import collections
7 import os
8 import re
9 import urllib2
10
11 CLANG_DIR = os.path.join(os.path.dirname(__file__), '../..')
12 FORMAT_STYLE_FILE = os.path.join(CLANG_DIR, 'include/clang/Format/Format.h')
13 INCLUDE_STYLE_FILE = os.path.join(CLANG_DIR, 'include/clang/Tooling/Inclusions/IncludeStyle.h')
14 DOC_FILE = os.path.join(CLANG_DIR, 'docs/ClangFormatStyleOptions.rst')
15
16
17 def substitute(text, tag, contents):
18   replacement = '\n.. START_%s\n\n%s\n\n.. END_%s\n' % (tag, contents, tag)
19   pattern = r'\n\.\. START_%s\n.*\n\.\. END_%s\n' % (tag, tag)
20   return re.sub(pattern, '%s', text, flags=re.S) % replacement
21
22 def doxygen2rst(text):
23   text = re.sub(r'<tt>\s*(.*?)\s*<\/tt>', r'``\1``', text)
24   text = re.sub(r'\\c ([^ ,;\.]+)', r'``\1``', text)
25   text = re.sub(r'\\\w+ ', '', text)
26   return text
27
28 def indent(text, columns, indent_first_line=True):
29   indent = ' ' * columns
30   s = re.sub(r'\n([^\n])', '\n' + indent + '\\1', text, flags=re.S)
31   if not indent_first_line or s.startswith('\n'):
32     return s
33   return indent + s
34
35 class Option:
36   def __init__(self, name, type, comment):
37     self.name = name
38     self.type = type
39     self.comment = comment.strip()
40     self.enum = None
41     self.nested_struct = None
42
43   def __str__(self):
44     s = '**%s** (``%s``)\n%s' % (self.name, self.type,
45                                  doxygen2rst(indent(self.comment, 2)))
46     if self.enum:
47       s += indent('\n\nPossible values:\n\n%s\n' % self.enum, 2)
48     if self.nested_struct:
49       s += indent('\n\nNested configuration flags:\n\n%s\n' %self.nested_struct,
50                   2)
51     return s
52
53 class NestedStruct:
54   def __init__(self, name, comment):
55     self.name = name
56     self.comment = comment.strip()
57     self.values = []
58
59   def __str__(self):
60     return '\n'.join(map(str, self.values))
61
62 class NestedField:
63   def __init__(self, name, comment):
64     self.name = name
65     self.comment = comment.strip()
66
67   def __str__(self):
68     return '\n* ``%s`` %s' % (
69         self.name,
70         doxygen2rst(indent(self.comment, 2, indent_first_line=False)))
71
72 class Enum:
73   def __init__(self, name, comment):
74     self.name = name
75     self.comment = comment.strip()
76     self.values = []
77
78   def __str__(self):
79     return '\n'.join(map(str, self.values))
80
81 class EnumValue:
82   def __init__(self, name, comment):
83     self.name = name
84     self.comment = comment
85
86   def __str__(self):
87     return '* ``%s`` (in configuration: ``%s``)\n%s' % (
88         self.name,
89         re.sub('.*_', '', self.name),
90         doxygen2rst(indent(self.comment, 2)))
91
92 def clean_comment_line(line):
93   match = re.match(r'^/// \\code(\{.(\w+)\})?$', line)
94   if match:
95     lang = match.groups()[1]
96     if not lang:
97       lang = 'c++'
98     return '\n.. code-block:: %s\n\n' % lang
99   if line == '/// \\endcode':
100     return ''
101   return line[4:] + '\n'
102
103 def read_options(header):
104   class State:
105     BeforeStruct, Finished, InStruct, InNestedStruct, InNestedFieldComent, \
106     InFieldComment, InEnum, InEnumMemberComment = range(8)
107   state = State.BeforeStruct
108
109   options = []
110   enums = {}
111   nested_structs = {}
112   comment = ''
113   enum = None
114   nested_struct = None
115
116   for line in header:
117     line = line.strip()
118     if state == State.BeforeStruct:
119       if line == 'struct FormatStyle {' or line == 'struct IncludeStyle {':
120         state = State.InStruct
121     elif state == State.InStruct:
122       if line.startswith('///'):
123         state = State.InFieldComment
124         comment = clean_comment_line(line)
125       elif line == '};':
126         state = State.Finished
127         break
128     elif state == State.InFieldComment:
129       if line.startswith('///'):
130         comment += clean_comment_line(line)
131       elif line.startswith('enum'):
132         state = State.InEnum
133         name = re.sub(r'enum\s+(\w+)\s*\{', '\\1', line)
134         enum = Enum(name, comment)
135       elif line.startswith('struct'):
136         state = State.InNestedStruct
137         name = re.sub(r'struct\s+(\w+)\s*\{', '\\1', line)
138         nested_struct = NestedStruct(name, comment)
139       elif line.endswith(';'):
140         state = State.InStruct
141         field_type, field_name = re.match(r'([<>:\w(,\s)]+)\s+(\w+);',
142                                           line).groups()
143         option = Option(str(field_name), str(field_type), comment)
144         options.append(option)
145       else:
146         raise Exception('Invalid format, expected comment, field or enum')
147     elif state == State.InNestedStruct:
148       if line.startswith('///'):
149         state = State.InNestedFieldComent
150         comment = clean_comment_line(line)
151       elif line == '};':
152         state = State.InStruct
153         nested_structs[nested_struct.name] = nested_struct
154     elif state == State.InNestedFieldComent:
155       if line.startswith('///'):
156         comment += clean_comment_line(line)
157       else:
158         state = State.InNestedStruct
159         nested_struct.values.append(NestedField(line.replace(';', ''), comment))
160     elif state == State.InEnum:
161       if line.startswith('///'):
162         state = State.InEnumMemberComment
163         comment = clean_comment_line(line)
164       elif line == '};':
165         state = State.InStruct
166         enums[enum.name] = enum
167       else:
168         raise Exception('Invalid format, expected enum field comment or };')
169     elif state == State.InEnumMemberComment:
170       if line.startswith('///'):
171         comment += clean_comment_line(line)
172       else:
173         state = State.InEnum
174         enum.values.append(EnumValue(line.replace(',', ''), comment))
175   if state != State.Finished:
176     raise Exception('Not finished by the end of file')
177
178   for option in options:
179     if not option.type in ['bool', 'unsigned', 'int', 'std::string',
180                            'std::vector<std::string>',
181                            'std::vector<IncludeCategory>',
182                            'std::vector<RawStringFormat>']:
183       if enums.has_key(option.type):
184         option.enum = enums[option.type]
185       elif nested_structs.has_key(option.type):
186         option.nested_struct = nested_structs[option.type]
187       else:
188         raise Exception('Unknown type: %s' % option.type)
189   return options
190
191 options = read_options(open(FORMAT_STYLE_FILE))
192 options += read_options(open(INCLUDE_STYLE_FILE))
193
194 options = sorted(options, key=lambda x: x.name)
195 options_text = '\n\n'.join(map(str, options))
196
197 contents = open(DOC_FILE).read()
198
199 contents = substitute(contents, 'FORMAT_STYLE_OPTIONS', options_text)
200
201 with open(DOC_FILE, 'wb') as output:
202   output.write(contents)