3 # Copyright Abel Sinkovics (abel@sinkovics.hu) 2015.
4 # Distributed under the Boost Software License, Version 1.0.
5 # (See accompanying file LICENSE_1_0.txt or copy at
6 # http://www.boost.org/LICENSE_1_0.txt)
13 def remove_last_dot(s
):
19 def remove_newline(s
):
20 return re
.sub('[\r\n]', '', s
)
25 def_prefixes
= ['#include ', 'using ', 'struct ', 'template ']
26 return any([cmd
.startswith(s
) for s
in def_prefixes
]) or cmd
.endswith(';')
28 def prefix_lines(prefix
, s
):
29 return '\n'.join(['%s%s' % (prefix
, l
) for l
in s
.split('\n')])
31 def protect_metashell(s
):
32 if s
.startswith('#include <metashell'):
33 return '#ifdef __METASHELL\n%s\n#endif' % (s
)
41 in_cpp_snippet
= False
42 numbered_section_header
= re
.compile('^\[section *([0-9.]+)')
43 metashell_command
= re
.compile('^> [^ ]')
44 metashell_prompt
= re
.compile('^(\.\.\.|)>')
49 if not in_cpp_snippet
:
50 in_msh_cpp_snippet
= True
51 if in_msh_cpp_snippet
:
52 if metashell_command
.match(ll
) or msh_cmd
!= '':
53 cmd
= metashell_prompt
.sub('', remove_newline(ll
))
55 msh_cmd
= msh_cmd
+ '\n'
56 msh_cmd
= msh_cmd
+ cmd
57 if msh_cmd
.endswith('\\'):
58 msh_cmd
= msh_cmd
[:-1].strip() + ' '
60 if not is_definition(msh_cmd
):
61 msh_cmd
= '// query:\n%s' % (prefix_lines('// ', msh_cmd
))
62 defs
.append((current_section
, protect_metashell(msh_cmd
.strip())))
64 elif not in_cpp_snippet
:
65 in_msh_cpp_snippet
= False
68 in_cpp_snippet
= False
69 m
= numbered_section_header
.match(l
)
71 current_section
= remove_last_dot(m
.group(1)).replace('.', '_')
72 sections
.append(current_section
)
74 sections
.sort(key
= lambda s
: [int(n
) for n
in s
.split('_')])
75 return (sections
, defs
)
77 def delete_old_headers(path
):
78 for f
in os
.listdir(path
):
79 if f
.endswith('.hpp'):
80 os
.remove(os
.path
.join(path
, f
))
82 def gen_headers(sections
, defs
, path
):
87 prev_name
= prev_section
.replace('_', '.')
88 include_guard
= 'BOOST_METAPARSE_GETTING_STARTED_%s_HPP' % (s
)
89 if prev_section
== '':
93 '// Definitions before section {0}\n'.format(prev_name
) + \
94 '#include "{0}.hpp"\n'.format(prev_section
) + \
97 files
[os
.path
.join(path
, s
+ '.hpp')] = \
98 '#ifndef {0}\n'.format(include_guard
) + \
99 '#define {0}\n'.format(include_guard
) + \
101 '// Automatically generated header file\n' + \
104 '// Definitions of section {0}\n'.format(prev_name
) + \
106 ['%s\n' % (d
) for (sec
, d
) in defs
if sec
== prev_section
] \
114 def remove_metashell_protection(s
):
115 prefix
= '#ifdef __METASHELL\n'
118 s
[len(prefix
):-len(suffix
)] \
119 if s
.startswith(prefix
) and s
.endswith(suffix
) \
122 def make_code_snippet(s
):
123 return '\n'.join([' {0}'.format(l
) for l
in s
.split('\n')])
125 def what_we_have_so_far_docs(doc_dir
, qbk
, defs
, sections
):
128 sections_with_definition
= []
131 files
[os
.path
.join(doc_dir
, 'before_{0}.qbk'.format(s
))] = \
132 '[#before_{0}]\n[\'Definitions before section {1}]\n\n{2}\n'.format(
134 s
.replace('_', '.') + '.',
137 sections_with_definition
.append(s
)
139 so_far
= so_far
+ '\n'.join([
140 '{0}\n'.format(make_code_snippet(remove_metashell_protection(d
)))
142 if sec
== s
and not d
.startswith('//')
145 is_section
= re
.compile('^\[section (([0-9]\.)+)')
147 '[note Note that you can find everything that has been included and' \
148 ' defined so far [link before_'
150 in_definitions_before_each_section
= False
154 if in_definitions_before_each_section
:
155 if l
.strip() == '[endsect]':
156 in_definitions_before_each_section
= False
158 elif l
.strip() == '[section Definitions before each section]':
159 in_definitions_before_each_section
= True
162 for s
in sections_with_definition
:
163 result
.append('[include before_{0}.qbk]\n'.format(s
))
165 elif not l
.startswith(note_prefix
):
167 m
= is_section
.match(l
)
169 section_number
= m
.group(1).replace('.', '_')[:-1]
170 if section_number
in sections_with_definition
:
171 result
.append('{0}{1} here].]\n'.format(note_prefix
, section_number
))
173 return (files
, result
)
175 def strip_not_finished_line(s
):
177 return s
[:-1] if s
.endswith('\\') else s
179 def make_copy_paste_friendly(lines
):
182 if l
.startswith('> '):
184 elif l
.startswith('...> '):
185 result
[-1] = strip_not_finished_line(result
[-1]) + l
[5:].lstrip()
188 def extract_code_snippets(qbk
, fn_base
):
196 in_copy_paste_friendly_examples
= False
197 skip_empty_lines
= False
199 if l
.strip() != '' or not skip_empty_lines
:
200 skip_empty_lines
= False
201 if in_copy_paste_friendly_examples
:
203 in_copy_paste_friendly_examples
= False
206 '[include {0}_{1}.qbk]\n'.format(re
.sub('^.*/', '', fn_base
), i
) \
207 for i
in range(0, counter
)
211 in_copy_paste_friendly_examples
= False
212 elif '[section Copy-paste friendly code examples]' in l
:
213 in_copy_paste_friendly_examples
= True
215 elif 'copy-paste friendly version' in l
:
216 skip_empty_lines
= True
221 if not l
.startswith(code_prefix
):
224 f
= '{0}_{1}'.format(fn_base
, counter
)
225 basename_f
= re
.sub('^.*/', '', f
)
226 files
['{0}.qbk'.format(f
)] = \
227 '[#{0}]\n\n{1}\n'.format(
230 [code_prefix
+ s
for s
in make_copy_paste_friendly(code
)]
234 '[link {0} copy-paste friendly version]\n'.format(basename_f
)
237 counter
= counter
+ 1
239 l
.startswith(code_prefix
+ '> ') \
240 or l
.startswith(code_prefix
+ '...> '):
241 code
.append(l
[len(code_prefix
):])
242 elif l
.startswith(code_prefix
):
244 code
= [l
[len(code_prefix
):]]
246 return (files
, result
)
248 def write_file(fn
, content
):
249 with
open(fn
, 'w') as f
:
252 def write_files(files
):
254 write_file(fn
, files
[fn
])
257 desc
= 'Generate headers with the definitions of a Getting Started guide'
258 parser
= argparse
.ArgumentParser(description
=desc
)
262 default
='doc/getting_started.qbk',
263 help='The .qbk source of the Getting Started guide'
268 default
='example/getting_started',
269 help='The target directory to generate into (all headers in that directory will be deleted!)'
272 args
= parser
.parse_args()
274 qbk
= open(args
.src
, 'r').readlines()
276 delete_old_headers(args
.dst
)
277 doc_dir
= os
.path
.dirname(args
.src
)
279 (sections
, defs
) = parse_md(qbk
)
280 files1
= gen_headers(sections
, defs
, args
.dst
)
281 (files2
, qbk
) = what_we_have_so_far_docs(doc_dir
, qbk
, defs
, sections
)
283 extract_code_snippets(
285 args
.src
[:-4] if args
.src
.endswith('.qbk') else args
.src
291 write_file(args
.src
, ''.join(qbk
))
293 if __name__
== "__main__":