]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | #!/usr/bin/python |
2 | """Utility to generate the header files for BOOST_METAPARSE_STRING""" | |
3 | ||
4 | # Copyright Abel Sinkovics (abel@sinkovics.hu) 2016. | |
5 | # Distributed under the Boost Software License, Version 1.0. | |
6 | # (See accompanying file LICENSE_1_0.txt or copy at | |
7 | # http://www.boost.org/LICENSE_1_0.txt) | |
8 | ||
9 | import argparse | |
10 | import math | |
11 | import os | |
12 | import sys | |
13 | ||
14 | ||
15 | VERSION = 1 | |
16 | ||
17 | ||
18 | class Namespace(object): | |
19 | """Generate namespace definition""" | |
20 | ||
21 | def __init__(self, out_f, names): | |
22 | self.out_f = out_f | |
23 | self.names = names | |
24 | ||
25 | def begin(self): | |
26 | """Generate the beginning part""" | |
27 | self.out_f.write('\n') | |
28 | for depth, name in enumerate(self.names): | |
29 | self.out_f.write( | |
30 | '{0}namespace {1}\n{0}{{\n'.format(self.prefix(depth), name) | |
31 | ) | |
32 | ||
33 | def end(self): | |
34 | """Generate the closing part""" | |
35 | for depth in xrange(len(self.names) - 1, -1, -1): | |
36 | self.out_f.write('{0}}}\n'.format(self.prefix(depth))) | |
37 | ||
38 | def prefix(self, depth=None): | |
39 | """Returns the prefix of a given depth. Returns the prefix code inside | |
40 | the namespace should use when depth is None.""" | |
41 | if depth is None: | |
42 | depth = len(self.names) | |
43 | return ' ' * depth | |
44 | ||
45 | def __enter__(self): | |
46 | self.begin() | |
47 | return self | |
48 | ||
49 | def __exit__(self, typ, value, traceback): | |
50 | self.end() | |
51 | ||
52 | ||
53 | def write_autogen_info(out_f): | |
54 | """Write the comment about the file being autogenerated""" | |
55 | out_f.write( | |
56 | '\n' | |
57 | '// This is an automatically generated header file.\n' | |
58 | '// Generated with the tools/string_headers.py utility of\n' | |
59 | '// Boost.Metaparse\n' | |
60 | ) | |
61 | ||
62 | ||
63 | class IncludeGuard(object): | |
64 | """Generate include guards""" | |
65 | ||
b32b8144 | 66 | def __init__(self, out_f): |
7c673cae | 67 | self.out_f = out_f |
7c673cae FG |
68 | |
69 | def begin(self): | |
70 | """Generate the beginning part""" | |
b32b8144 | 71 | name = 'BOOST_METAPARSE_V1_CPP11_IMPL_STRING_HPP' |
7c673cae FG |
72 | self.out_f.write('#ifndef {0}\n#define {0}\n'.format(name)) |
73 | write_autogen_info(self.out_f) | |
74 | ||
75 | def end(self): | |
76 | """Generate the closing part""" | |
77 | self.out_f.write('\n#endif\n') | |
78 | ||
79 | def __enter__(self): | |
80 | self.begin() | |
81 | return self | |
82 | ||
83 | def __exit__(self, typ, value, traceback): | |
84 | self.end() | |
85 | ||
86 | ||
87 | def macro_name(name): | |
88 | """Generate the full macro name""" | |
89 | return 'BOOST_METAPARSE_V{0}_{1}'.format(VERSION, name) | |
90 | ||
91 | ||
92 | def define_macro(out_f, (name, args, body), undefine=False, check=True): | |
93 | """Generate a macro definition or undefinition""" | |
94 | if undefine: | |
95 | out_f.write( | |
96 | '#undef {0}\n' | |
97 | .format(macro_name(name)) | |
98 | ) | |
99 | else: | |
b32b8144 | 100 | if args: |
7c673cae FG |
101 | arg_list = '({0})'.format(', '.join(args)) |
102 | else: | |
103 | arg_list = '' | |
104 | ||
105 | if check: | |
106 | out_f.write( | |
107 | '#ifdef {0}\n' | |
108 | '# error {0} already defined.\n' | |
109 | '#endif\n' | |
110 | .format(macro_name(name)) | |
111 | ) | |
112 | ||
113 | out_f.write( | |
114 | '#define {0}{1} {2}\n'.format(macro_name(name), arg_list, body) | |
115 | ) | |
116 | ||
117 | ||
118 | def filename(out_dir, name, undefine=False): | |
119 | """Generate the filename""" | |
120 | if undefine: | |
121 | prefix = 'undef_' | |
122 | else: | |
123 | prefix = '' | |
124 | return os.path.join(out_dir, '{0}{1}.hpp'.format(prefix, name.lower())) | |
125 | ||
126 | ||
127 | def length_limits(max_length_limit, length_limit_step): | |
128 | """Generates the length limits""" | |
129 | string_len = len(str(max_length_limit)) | |
130 | return [ | |
131 | str(i).zfill(string_len) for i in | |
132 | xrange( | |
133 | length_limit_step, | |
134 | max_length_limit + length_limit_step - 1, | |
135 | length_limit_step | |
136 | ) | |
137 | ] | |
138 | ||
139 | ||
140 | def unique_names(count): | |
141 | """Generate count unique variable name""" | |
142 | return ('C{0}'.format(i) for i in xrange(0, count)) | |
143 | ||
144 | ||
145 | def generate_take(out_f, steps, line_prefix): | |
146 | """Generate the take function""" | |
147 | out_f.write( | |
148 | '{0}constexpr inline int take(int n_)\n' | |
149 | '{0}{{\n' | |
150 | '{0} return {1} 0 {2};\n' | |
151 | '{0}}}\n' | |
152 | '\n'.format( | |
153 | line_prefix, | |
154 | ''.join('n_ >= {0} ? {0} : ('.format(s) for s in steps), | |
155 | ')' * len(steps) | |
156 | ) | |
157 | ) | |
158 | ||
159 | ||
160 | def generate_make_string(out_f, max_step): | |
161 | """Generate the make_string template""" | |
162 | steps = [2 ** n for n in xrange(int(math.log(max_step, 2)), -1, -1)] | |
163 | ||
164 | with Namespace( | |
165 | out_f, | |
166 | ['boost', 'metaparse', 'v{0}'.format(VERSION), 'impl'] | |
167 | ) as nsp: | |
168 | generate_take(out_f, steps, nsp.prefix()) | |
169 | ||
170 | out_f.write( | |
171 | '{0}template <int LenNow, int LenRemaining, char... Cs>\n' | |
172 | '{0}struct make_string;\n' | |
173 | '\n' | |
174 | '{0}template <char... Cs>' | |
175 | ' struct make_string<0, 0, Cs...> : string<> {{}};\n' | |
176 | .format(nsp.prefix()) | |
177 | ) | |
178 | ||
179 | disable_sun = False | |
180 | for i in reversed(steps): | |
181 | if i > 64 and not disable_sun: | |
182 | out_f.write('#ifndef __SUNPRO_CC\n') | |
183 | disable_sun = True | |
184 | out_f.write( | |
185 | '{0}template <int LenRemaining,{1}char... Cs>' | |
186 | ' struct make_string<{2},LenRemaining,{3}Cs...> :' | |
187 | ' concat<string<{4}>,' | |
188 | ' typename make_string<take(LenRemaining),' | |
189 | 'LenRemaining-take(LenRemaining),Cs...>::type> {{}};\n' | |
190 | .format( | |
191 | nsp.prefix(), | |
192 | ''.join('char {0},'.format(n) for n in unique_names(i)), | |
193 | i, | |
194 | ''.join('{0},'.format(n) for n in unique_names(i)), | |
195 | ','.join(unique_names(i)) | |
196 | ) | |
197 | ) | |
198 | if disable_sun: | |
199 | out_f.write('#endif\n') | |
200 | ||
201 | ||
202 | def generate_string(out_dir, limits): | |
203 | """Generate string.hpp""" | |
204 | max_limit = max((int(v) for v in limits)) | |
205 | ||
206 | with open(filename(out_dir, 'string'), 'wb') as out_f: | |
b32b8144 | 207 | with IncludeGuard(out_f): |
7c673cae FG |
208 | out_f.write( |
209 | '\n' | |
b32b8144 | 210 | '#include <boost/metaparse/v{0}/cpp11/impl/concat.hpp>\n' |
7c673cae FG |
211 | '#include <boost/preprocessor/cat.hpp>\n' |
212 | .format(VERSION) | |
213 | ) | |
214 | ||
215 | generate_make_string(out_f, 512) | |
216 | ||
217 | out_f.write( | |
218 | '\n' | |
219 | '#ifndef BOOST_METAPARSE_LIMIT_STRING_SIZE\n' | |
220 | '# error BOOST_METAPARSE_LIMIT_STRING_SIZE not defined\n' | |
221 | '#endif\n' | |
222 | '\n' | |
223 | '#if BOOST_METAPARSE_LIMIT_STRING_SIZE > {0}\n' | |
224 | '# error BOOST_METAPARSE_LIMIT_STRING_SIZE is greater than' | |
225 | ' {0}. To increase the limit run tools/string_headers.py of' | |
226 | ' Boost.Metaparse against your Boost headers.\n' | |
227 | '#endif\n' | |
228 | '\n' | |
229 | .format(max_limit) | |
230 | ) | |
231 | ||
232 | define_macro(out_f, ( | |
233 | 'STRING', | |
234 | ['s'], | |
235 | '{0}::make_string< ' | |
236 | '{0}::take(sizeof(s)-1), sizeof(s)-1-{0}::take(sizeof(s)-1),' | |
237 | 'BOOST_PP_CAT({1}, BOOST_METAPARSE_LIMIT_STRING_SIZE)(s)' | |
238 | '>::type' | |
239 | .format( | |
240 | '::boost::metaparse::v{0}::impl'.format(VERSION), | |
241 | macro_name('I') | |
242 | ) | |
243 | )) | |
244 | ||
245 | out_f.write('\n') | |
246 | for limit in xrange(0, max_limit + 1): | |
247 | out_f.write( | |
248 | '#define {0} {1}\n' | |
249 | .format( | |
250 | macro_name('I{0}'.format(limit)), | |
251 | macro_name('INDEX_STR{0}'.format( | |
252 | min(int(l) for l in limits if int(l) >= limit) | |
253 | )) | |
254 | ) | |
255 | ) | |
256 | out_f.write('\n') | |
257 | ||
258 | prev_macro = None | |
259 | prev_limit = 0 | |
260 | for length_limit in (int(l) for l in limits): | |
261 | this_macro = macro_name('INDEX_STR{0}'.format(length_limit)) | |
262 | out_f.write( | |
263 | '#define {0}(s) {1}{2}\n' | |
264 | .format( | |
265 | this_macro, | |
266 | '{0}(s),'.format(prev_macro) if prev_macro else '', | |
267 | ','.join( | |
268 | '{0}((s), {1})' | |
269 | .format(macro_name('STRING_AT'), i) | |
270 | for i in xrange(prev_limit, length_limit) | |
271 | ) | |
272 | ) | |
273 | ) | |
274 | prev_macro = this_macro | |
275 | prev_limit = length_limit | |
276 | ||
277 | ||
278 | def positive_integer(value): | |
279 | """Throws when the argument is not a positive integer""" | |
280 | val = int(value) | |
281 | if val > 0: | |
282 | return val | |
283 | else: | |
284 | raise argparse.ArgumentTypeError("A positive number is expected") | |
285 | ||
286 | ||
287 | def existing_path(value): | |
288 | """Throws when the path does not exist""" | |
289 | if os.path.exists(value): | |
290 | return value | |
291 | else: | |
292 | raise argparse.ArgumentTypeError("Path {0} not found".format(value)) | |
293 | ||
294 | ||
295 | def main(): | |
296 | """The main function of the script""" | |
297 | parser = argparse.ArgumentParser(description=__doc__) | |
298 | parser.add_argument( | |
299 | '--boost_dir', | |
300 | required=False, | |
301 | type=existing_path, | |
302 | help='The path to the include/boost directory of Metaparse' | |
303 | ) | |
304 | parser.add_argument( | |
305 | '--max_length_limit', | |
306 | required=False, | |
307 | default=2048, | |
308 | type=positive_integer, | |
309 | help='The maximum supported length limit' | |
310 | ) | |
311 | parser.add_argument( | |
312 | '--length_limit_step', | |
313 | required=False, | |
314 | default=128, | |
315 | type=positive_integer, | |
316 | help='The longest step at which headers are generated' | |
317 | ) | |
318 | args = parser.parse_args() | |
319 | ||
320 | if args.boost_dir is None: | |
321 | tools_path = os.path.dirname(os.path.abspath(__file__)) | |
322 | boost_dir = os.path.join( | |
323 | os.path.dirname(tools_path), | |
324 | 'include', | |
325 | 'boost' | |
326 | ) | |
327 | else: | |
328 | boost_dir = args.boost_dir | |
329 | ||
330 | if args.max_length_limit < 1: | |
331 | sys.stderr.write('Invalid maximum length limit') | |
332 | sys.exit(-1) | |
333 | ||
334 | generate_string( | |
b32b8144 FG |
335 | os.path.join( |
336 | boost_dir, | |
337 | 'metaparse', | |
338 | 'v{0}'.format(VERSION), | |
339 | 'cpp11', | |
340 | 'impl' | |
341 | ), | |
7c673cae FG |
342 | length_limits(args.max_length_limit, args.length_limit_step) |
343 | ) | |
344 | ||
345 | ||
346 | if __name__ == '__main__': | |
347 | main() |