348 lines
10 KiB
Python
Executable File
348 lines
10 KiB
Python
Executable File
#!/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()
|