123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- #!/usr/bin/python
- """Utility to generate the header files for BOOST_METAPARSE_STRING"""
- # Copyright Abel Sinkovics (abel@sinkovics.hu) 2016.
- # Distributed under the Boost Software License, Version 1.0.
- # (See accompanying file LICENSE_1_0.txt or copy at
- # http://www.boost.org/LICENSE_1_0.txt)
- import argparse
- import math
- import os
- import sys
- VERSION = 1
- class Namespace(object):
- """Generate namespace definition"""
- def __init__(self, out_f, names):
- self.out_f = out_f
- self.names = names
- def begin(self):
- """Generate the beginning part"""
- self.out_f.write('\n')
- for depth, name in enumerate(self.names):
- self.out_f.write(
- '{0}namespace {1}\n{0}{{\n'.format(self.prefix(depth), name)
- )
- def end(self):
- """Generate the closing part"""
- for depth in xrange(len(self.names) - 1, -1, -1):
- self.out_f.write('{0}}}\n'.format(self.prefix(depth)))
- def prefix(self, depth=None):
- """Returns the prefix of a given depth. Returns the prefix code inside
- the namespace should use when depth is None."""
- if depth is None:
- depth = len(self.names)
- return ' ' * depth
- def __enter__(self):
- self.begin()
- return self
- def __exit__(self, typ, value, traceback):
- self.end()
- def write_autogen_info(out_f):
- """Write the comment about the file being autogenerated"""
- out_f.write(
- '\n'
- '// This is an automatically generated header file.\n'
- '// Generated with the tools/string_headers.py utility of\n'
- '// Boost.Metaparse\n'
- )
- class IncludeGuard(object):
- """Generate include guards"""
- def __init__(self, out_f):
- self.out_f = out_f
- def begin(self):
- """Generate the beginning part"""
- name = 'BOOST_METAPARSE_V1_CPP11_IMPL_STRING_HPP'
- self.out_f.write('#ifndef {0}\n#define {0}\n'.format(name))
- write_autogen_info(self.out_f)
- def end(self):
- """Generate the closing part"""
- self.out_f.write('\n#endif\n')
- def __enter__(self):
- self.begin()
- return self
- def __exit__(self, typ, value, traceback):
- self.end()
- def macro_name(name):
- """Generate the full macro name"""
- return 'BOOST_METAPARSE_V{0}_{1}'.format(VERSION, name)
- def define_macro(out_f, (name, args, body), undefine=False, check=True):
- """Generate a macro definition or undefinition"""
- if undefine:
- out_f.write(
- '#undef {0}\n'
- .format(macro_name(name))
- )
- else:
- if args:
- arg_list = '({0})'.format(', '.join(args))
- else:
- arg_list = ''
- if check:
- out_f.write(
- '#ifdef {0}\n'
- '# error {0} already defined.\n'
- '#endif\n'
- .format(macro_name(name))
- )
- out_f.write(
- '#define {0}{1} {2}\n'.format(macro_name(name), arg_list, body)
- )
- def filename(out_dir, name, undefine=False):
- """Generate the filename"""
- if undefine:
- prefix = 'undef_'
- else:
- prefix = ''
- return os.path.join(out_dir, '{0}{1}.hpp'.format(prefix, name.lower()))
- def length_limits(max_length_limit, length_limit_step):
- """Generates the length limits"""
- string_len = len(str(max_length_limit))
- return [
- str(i).zfill(string_len) for i in
- xrange(
- length_limit_step,
- max_length_limit + length_limit_step - 1,
- length_limit_step
- )
- ]
- def unique_names(count):
- """Generate count unique variable name"""
- return ('C{0}'.format(i) for i in xrange(0, count))
- def generate_take(out_f, steps, line_prefix):
- """Generate the take function"""
- out_f.write(
- '{0}constexpr inline int take(int n_)\n'
- '{0}{{\n'
- '{0} return {1} 0 {2};\n'
- '{0}}}\n'
- '\n'.format(
- line_prefix,
- ''.join('n_ >= {0} ? {0} : ('.format(s) for s in steps),
- ')' * len(steps)
- )
- )
- def generate_make_string(out_f, max_step):
- """Generate the make_string template"""
- steps = [2 ** n for n in xrange(int(math.log(max_step, 2)), -1, -1)]
- with Namespace(
- out_f,
- ['boost', 'metaparse', 'v{0}'.format(VERSION), 'impl']
- ) as nsp:
- generate_take(out_f, steps, nsp.prefix())
- out_f.write(
- '{0}template <int LenNow, int LenRemaining, char... Cs>\n'
- '{0}struct make_string;\n'
- '\n'
- '{0}template <char... Cs>'
- ' struct make_string<0, 0, Cs...> : string<> {{}};\n'
- .format(nsp.prefix())
- )
- disable_sun = False
- for i in reversed(steps):
- if i > 64 and not disable_sun:
- out_f.write('#ifndef __SUNPRO_CC\n')
- disable_sun = True
- out_f.write(
- '{0}template <int LenRemaining,{1}char... Cs>'
- ' struct make_string<{2},LenRemaining,{3}Cs...> :'
- ' concat<string<{4}>,'
- ' typename make_string<take(LenRemaining),'
- 'LenRemaining-take(LenRemaining),Cs...>::type> {{}};\n'
- .format(
- nsp.prefix(),
- ''.join('char {0},'.format(n) for n in unique_names(i)),
- i,
- ''.join('{0},'.format(n) for n in unique_names(i)),
- ','.join(unique_names(i))
- )
- )
- if disable_sun:
- out_f.write('#endif\n')
- def generate_string(out_dir, limits):
- """Generate string.hpp"""
- max_limit = max((int(v) for v in limits))
- with open(filename(out_dir, 'string'), 'wb') as out_f:
- with IncludeGuard(out_f):
- out_f.write(
- '\n'
- '#include <boost/metaparse/v{0}/cpp11/impl/concat.hpp>\n'
- '#include <boost/preprocessor/cat.hpp>\n'
- .format(VERSION)
- )
- generate_make_string(out_f, 512)
- out_f.write(
- '\n'
- '#ifndef BOOST_METAPARSE_LIMIT_STRING_SIZE\n'
- '# error BOOST_METAPARSE_LIMIT_STRING_SIZE not defined\n'
- '#endif\n'
- '\n'
- '#if BOOST_METAPARSE_LIMIT_STRING_SIZE > {0}\n'
- '# error BOOST_METAPARSE_LIMIT_STRING_SIZE is greater than'
- ' {0}. To increase the limit run tools/string_headers.py of'
- ' Boost.Metaparse against your Boost headers.\n'
- '#endif\n'
- '\n'
- .format(max_limit)
- )
- define_macro(out_f, (
- 'STRING',
- ['s'],
- '{0}::make_string< '
- '{0}::take(sizeof(s)-1), sizeof(s)-1-{0}::take(sizeof(s)-1),'
- 'BOOST_PP_CAT({1}, BOOST_METAPARSE_LIMIT_STRING_SIZE)(s)'
- '>::type'
- .format(
- '::boost::metaparse::v{0}::impl'.format(VERSION),
- macro_name('I')
- )
- ))
- out_f.write('\n')
- for limit in xrange(0, max_limit + 1):
- out_f.write(
- '#define {0} {1}\n'
- .format(
- macro_name('I{0}'.format(limit)),
- macro_name('INDEX_STR{0}'.format(
- min(int(l) for l in limits if int(l) >= limit)
- ))
- )
- )
- out_f.write('\n')
- prev_macro = None
- prev_limit = 0
- for length_limit in (int(l) for l in limits):
- this_macro = macro_name('INDEX_STR{0}'.format(length_limit))
- out_f.write(
- '#define {0}(s) {1}{2}\n'
- .format(
- this_macro,
- '{0}(s),'.format(prev_macro) if prev_macro else '',
- ','.join(
- '{0}((s), {1})'
- .format(macro_name('STRING_AT'), i)
- for i in xrange(prev_limit, length_limit)
- )
- )
- )
- prev_macro = this_macro
- prev_limit = length_limit
- def positive_integer(value):
- """Throws when the argument is not a positive integer"""
- val = int(value)
- if val > 0:
- return val
- else:
- raise argparse.ArgumentTypeError("A positive number is expected")
- def existing_path(value):
- """Throws when the path does not exist"""
- if os.path.exists(value):
- return value
- else:
- raise argparse.ArgumentTypeError("Path {0} not found".format(value))
- def main():
- """The main function of the script"""
- parser = argparse.ArgumentParser(description=__doc__)
- parser.add_argument(
- '--boost_dir',
- required=False,
- type=existing_path,
- help='The path to the include/boost directory of Metaparse'
- )
- parser.add_argument(
- '--max_length_limit',
- required=False,
- default=2048,
- type=positive_integer,
- help='The maximum supported length limit'
- )
- parser.add_argument(
- '--length_limit_step',
- required=False,
- default=128,
- type=positive_integer,
- help='The longest step at which headers are generated'
- )
- args = parser.parse_args()
- if args.boost_dir is None:
- tools_path = os.path.dirname(os.path.abspath(__file__))
- boost_dir = os.path.join(
- os.path.dirname(tools_path),
- 'include',
- 'boost'
- )
- else:
- boost_dir = args.boost_dir
- if args.max_length_limit < 1:
- sys.stderr.write('Invalid maximum length limit')
- sys.exit(-1)
- generate_string(
- os.path.join(
- boost_dir,
- 'metaparse',
- 'v{0}'.format(VERSION),
- 'cpp11',
- 'impl'
- ),
- length_limits(args.max_length_limit, args.length_limit_step)
- )
- if __name__ == '__main__':
- main()
|