123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295 |
- #!/usr/bin/python
- # Copyright Abel Sinkovics (abel@sinkovics.hu) 2015.
- # 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 sys
- import argparse
- import re
- import os
- def remove_last_dot(s):
- if s.endswith('.'):
- return s[:-1]
- else:
- return s
- def remove_newline(s):
- return re.sub('[\r\n]', '', s)
- def is_definition(s):
- cmd = s.strip()
-
- def_prefixes = ['#include ', 'using ', 'struct ', 'template ']
- return any([cmd.startswith(s) for s in def_prefixes]) or cmd.endswith(';')
- def prefix_lines(prefix, s):
- return '\n'.join(['%s%s' % (prefix, l) for l in s.split('\n')])
- def protect_metashell(s):
- if s.startswith('#include <metashell'):
- return '#ifdef __METASHELL\n%s\n#endif' % (s)
- else:
- return s
- def parse_md(qbk):
- sections = []
- defs = []
- current_section = ''
- in_cpp_snippet = False
- numbered_section_header = re.compile('^\[section *([0-9.]+)')
- metashell_command = re.compile('^> [^ ]')
- metashell_prompt = re.compile('^(\.\.\.|)>')
- msh_cmd = ''
- for l in qbk:
- if l.startswith(' '):
- ll = l[2:]
- if not in_cpp_snippet:
- in_msh_cpp_snippet = True
- if in_msh_cpp_snippet:
- if metashell_command.match(ll) or msh_cmd != '':
- cmd = metashell_prompt.sub('', remove_newline(ll))
- if msh_cmd != '':
- msh_cmd = msh_cmd + '\n'
- msh_cmd = msh_cmd + cmd
- if msh_cmd.endswith('\\'):
- msh_cmd = msh_cmd[:-1].strip() + ' '
- else:
- if not is_definition(msh_cmd):
- msh_cmd = '// query:\n%s' % (prefix_lines('// ', msh_cmd))
- defs.append((current_section, protect_metashell(msh_cmd.strip())))
- msh_cmd = ''
- elif not in_cpp_snippet:
- in_msh_cpp_snippet = False
- in_cpp_snippet = True
- else:
- in_cpp_snippet = False
- m = numbered_section_header.match(l)
- if m:
- current_section = remove_last_dot(m.group(1)).replace('.', '_')
- sections.append(current_section)
- sections.sort(key = lambda s: [int(n) for n in s.split('_')])
- return (sections, defs)
- def delete_old_headers(path):
- for f in os.listdir(path):
- if f.endswith('.hpp'):
- os.remove(os.path.join(path, f))
- def gen_headers(sections, defs, path):
- files = {}
- prev_section = ''
- for s in sections:
- prev_name = prev_section.replace('_', '.')
- include_guard = 'BOOST_METAPARSE_GETTING_STARTED_%s_HPP' % (s)
- if prev_section == '':
- prev_include = ''
- else:
- prev_include = \
- '// Definitions before section {0}\n'.format(prev_name) + \
- '#include "{0}.hpp"\n'.format(prev_section) + \
- '\n'
- files[os.path.join(path, s + '.hpp')] = \
- '#ifndef {0}\n'.format(include_guard) + \
- '#define {0}\n'.format(include_guard) + \
- '\n' + \
- '// Automatically generated header file\n' + \
- '\n' + \
- prev_include + \
- '// Definitions of section {0}\n'.format(prev_name) + \
- '\n'.join( \
- ['%s\n' % (d) for (sec, d) in defs if sec == prev_section] \
- ) + \
- '\n' + \
- '#endif\n' + \
- '\n'
- prev_section = s
- return files
- def remove_metashell_protection(s):
- prefix = '#ifdef __METASHELL\n'
- suffix = '#endif'
- return \
- s[len(prefix):-len(suffix)] \
- if s.startswith(prefix) and s.endswith(suffix) \
- else s
- def make_code_snippet(s):
- return '\n'.join([' {0}'.format(l) for l in s.split('\n')])
- def what_we_have_so_far_docs(doc_dir, qbk, defs, sections):
- files = {}
- so_far = ''
- sections_with_definition = []
- for s in sections:
- if so_far != '':
- files[os.path.join(doc_dir, 'before_{0}.qbk'.format(s))] = \
- '[#before_{0}]\n[\'Definitions before section {1}]\n\n{2}\n'.format(
- s,
- s.replace('_', '.') + '.',
- so_far
- )
- sections_with_definition.append(s)
- so_far = so_far + '\n'.join([
- '{0}\n'.format(make_code_snippet(remove_metashell_protection(d)))
- for (sec, d) in defs
- if sec == s and not d.startswith('//')
- ])
- is_section = re.compile('^\[section (([0-9]\.)+)')
- note_prefix = \
- '[note Note that you can find everything that has been included and' \
- ' defined so far [link before_'
- in_definitions_before_each_section = False
- result = []
- for l in qbk:
- if in_definitions_before_each_section:
- if l.strip() == '[endsect]':
- in_definitions_before_each_section = False
- result.append(l)
- elif l.strip() == '[section Definitions before each section]':
- in_definitions_before_each_section = True
- result.append(l)
- result.append('\n')
- for s in sections_with_definition:
- result.append('[include before_{0}.qbk]\n'.format(s))
- result.append('\n')
- elif not l.startswith(note_prefix):
- result.append(l)
- m = is_section.match(l)
- if m:
- section_number = m.group(1).replace('.', '_')[:-1]
- if section_number in sections_with_definition:
- result.append('{0}{1} here].]\n'.format(note_prefix, section_number))
- return (files, result)
- def strip_not_finished_line(s):
- s = s.strip()
- return s[:-1] if s.endswith('\\') else s
- def make_copy_paste_friendly(lines):
- result = []
- for l in lines:
- if l.startswith('> '):
- result.append(l[2:])
- elif l.startswith('...> '):
- result[-1] = strip_not_finished_line(result[-1]) + l[5:].lstrip()
- return result
- def extract_code_snippets(qbk, fn_base):
- code_prefix = ' '
- files = {}
- result = []
- in_cpp_code = False
- counter = 0
- in_copy_paste_friendly_examples = False
- skip_empty_lines = False
- for l in qbk:
- if l.strip() != '' or not skip_empty_lines:
- skip_empty_lines = False
- if in_copy_paste_friendly_examples:
- if 'endsect' in l:
- in_copy_paste_friendly_examples = False
- result.append('\n')
- result.extend([
- '[include {0}_{1}.qbk]\n'.format(re.sub('^.*/', '', fn_base), i) \
- for i in range(0, counter)
- ])
- result.append('\n')
- result.append(l)
- in_copy_paste_friendly_examples = False
- elif '[section Copy-paste friendly code examples]' in l:
- in_copy_paste_friendly_examples = True
- result.append(l)
- elif 'copy-paste friendly version' in l:
- skip_empty_lines = True
- else:
- result.append(l)
-
- if in_cpp_code:
- if not l.startswith(code_prefix):
- in_cpp_code = False
- if len(code) > 1:
- f = '{0}_{1}'.format(fn_base, counter)
- basename_f = re.sub('^.*/', '', f)
- files['{0}.qbk'.format(f)] = \
- '[#{0}]\n\n{1}\n'.format(
- basename_f,
- ''.join(
- [code_prefix + s for s in make_copy_paste_friendly(code)]
- )
- )
- result.append(
- '[link {0} copy-paste friendly version]\n'.format(basename_f)
- )
- result.append('\n')
- counter = counter + 1
- elif \
- l.startswith(code_prefix + '> ') \
- or l.startswith(code_prefix + '...> '):
- code.append(l[len(code_prefix):])
- elif l.startswith(code_prefix):
- in_cpp_code = True
- code = [l[len(code_prefix):]]
- return (files, result)
- def write_file(fn, content):
- with open(fn, 'w') as f:
- f.write(content)
- def write_files(files):
- for fn in files:
- write_file(fn, files[fn])
- def main():
- desc = 'Generate headers with the definitions of a Getting Started guide'
- parser = argparse.ArgumentParser(description=desc)
- parser.add_argument(
- '--src',
- dest='src',
- default='doc/getting_started.qbk',
- help='The .qbk source of the Getting Started guide'
- )
- parser.add_argument(
- '--dst',
- dest='dst',
- default='example/getting_started',
- help='The target directory to generate into (all headers in that directory will be deleted!)'
- )
- args = parser.parse_args()
- qbk = open(args.src, 'r').readlines()
- delete_old_headers(args.dst)
- doc_dir = os.path.dirname(args.src)
- (sections, defs) = parse_md(qbk)
- files1 = gen_headers(sections, defs, args.dst)
- (files2, qbk) = what_we_have_so_far_docs(doc_dir, qbk, defs, sections)
- (files3, qbk) = \
- extract_code_snippets(
- qbk,
- args.src[:-4] if args.src.endswith('.qbk') else args.src
- )
- write_files(files1)
- write_files(files2)
- write_files(files3)
- write_file(args.src, ''.join(qbk))
- if __name__ == "__main__":
- main()
|