]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/tools/quickbook/src/code_snippet.cpp
add subtree-ish sources for 12.0.3
[ceph.git] / ceph / src / boost / tools / quickbook / src / code_snippet.cpp
CommitLineData
7c673cae
FG
1/*=============================================================================
2 Copyright (c) 2006 Joel de Guzman
3 http://spirit.sourceforge.net/
4
5 Use, modification and distribution is subject to the Boost Software
6 License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
7 http://www.boost.org/LICENSE_1_0.txt)
8=============================================================================*/
9
10#include <boost/spirit/include/classic_core.hpp>
11#include <boost/spirit/include/classic_actor.hpp>
12#include <boost/spirit/include/classic_confix.hpp>
13#include <boost/shared_ptr.hpp>
14#include <boost/bind.hpp>
15#include "block_tags.hpp"
16#include "template_stack.hpp"
17#include "actions.hpp"
18#include "state.hpp"
19#include "values.hpp"
20#include "files.hpp"
21#include "native_text.hpp"
22
23namespace quickbook
24{
25 namespace cl = boost::spirit::classic;
26
27 struct code_snippet_actions
28 {
29 code_snippet_actions(std::vector<template_symbol>& storage,
30 file_ptr source_file,
31 char const* source_type)
32 : last_code_pos(source_file->source().begin())
33 , in_code(false)
34 , snippet_stack()
35 , storage(storage)
36 , source_file(source_file)
37 , source_type(source_type)
38 , error_count(0)
39 {
40 source_file->is_code_snippets = true;
41 content.start(source_file);
42 }
43
44 void mark(string_iterator first, string_iterator last);
45 void pass_thru(string_iterator first, string_iterator last);
46 void escaped_comment(string_iterator first, string_iterator last);
47 void start_snippet(string_iterator first, string_iterator last);
48 void start_snippet_impl(std::string const&, string_iterator);
49 void end_snippet(string_iterator first, string_iterator last);
50 void end_snippet_impl(string_iterator);
51 void end_file(string_iterator, string_iterator);
52
53 void append_code(string_iterator first, string_iterator last);
54 void close_code();
55
56 struct snippet_data
57 {
58 snippet_data(std::string const& id)
59 : id(id)
60 , start_code(false)
61 {}
62
63 std::string id;
64 bool start_code;
65 string_iterator source_pos;
66 mapped_file_builder::pos start_pos;
67 boost::shared_ptr<snippet_data> next;
68 };
69
70 void push_snippet_data(std::string const& id,
71 string_iterator pos)
72 {
73 boost::shared_ptr<snippet_data> new_snippet(new snippet_data(id));
74 new_snippet->next = snippet_stack;
75 snippet_stack = new_snippet;
76 snippet_stack->start_code = in_code;
77 snippet_stack->source_pos = pos;
78 snippet_stack->start_pos = content.get_pos();
79 }
80
81 boost::shared_ptr<snippet_data> pop_snippet_data()
82 {
83 boost::shared_ptr<snippet_data> snippet(snippet_stack);
84 snippet_stack = snippet->next;
85 snippet->next.reset();
86 return snippet;
87 }
88
89 mapped_file_builder content;
90 boost::string_ref::const_iterator mark_begin, mark_end;
91 boost::string_ref::const_iterator last_code_pos;
92 bool in_code;
93 boost::shared_ptr<snippet_data> snippet_stack;
94 std::vector<template_symbol>& storage;
95 file_ptr source_file;
96 char const* const source_type;
97 int error_count;
98 };
99
100 struct python_code_snippet_grammar
101 : cl::grammar<python_code_snippet_grammar>
102 {
103 typedef code_snippet_actions actions_type;
104
105 python_code_snippet_grammar(actions_type & actions)
106 : actions(actions)
107 {}
108
109 template <typename Scanner>
110 struct definition
111 {
112 typedef code_snippet_actions actions_type;
113
114 definition(python_code_snippet_grammar const& self)
115 {
116
117 actions_type& actions = self.actions;
118
119 start_ = (*code_elements) [boost::bind(&actions_type::end_file, &actions, _1, _2)]
120 ;
121
122 identifier =
123 (cl::alpha_p | '_') >> *(cl::alnum_p | '_')
124 ;
125
126 code_elements =
127 start_snippet [boost::bind(&actions_type::start_snippet, &actions, _1, _2)]
128 | end_snippet [boost::bind(&actions_type::end_snippet, &actions, _1, _2)]
129 | escaped_comment [boost::bind(&actions_type::escaped_comment, &actions, _1, _2)]
130 | pass_thru_comment [boost::bind(&actions_type::pass_thru, &actions, _1, _2)]
131 | ignore [boost::bind(&actions_type::append_code, &actions, _1, _2)]
132 | cl::anychar_p
133 ;
134
135 start_snippet =
136 *cl::blank_p
137 >> !(cl::eol_p >> *cl::blank_p)
138 >> "#["
139 >> *cl::blank_p
140 >> identifier [boost::bind(&actions_type::mark, &actions, _1, _2)]
141 >> *(cl::anychar_p - cl::eol_p)
142 ;
143
144 end_snippet =
145 *cl::blank_p
146 >> !(cl::eol_p >> *cl::blank_p)
147 >> "#]"
148 >> *(cl::anychar_p - cl::eol_p)
149 ;
150
151 ignore
152 = cl::confix_p(
153 *cl::blank_p >> "#<-",
154 *cl::anychar_p,
155 "#->" >> *cl::blank_p >> (cl::eol_p | cl::end_p)
156 )
157 | cl::confix_p(
158 "\"\"\"<-\"\"\"",
159 *cl::anychar_p,
160 "\"\"\"->\"\"\""
161 )
162 | cl::confix_p(
163 "\"\"\"<-",
164 *cl::anychar_p,
165 "->\"\"\""
166 )
167 ;
168
169 escaped_comment =
170 cl::confix_p(
171 *cl::space_p >> "#`",
172 (*cl::anychar_p) [boost::bind(&actions_type::mark, &actions, _1, _2)],
173 (cl::eol_p | cl::end_p)
174 )
175 | cl::confix_p(
176 *cl::space_p >> "\"\"\"`",
177 (*cl::anychar_p) [boost::bind(&actions_type::mark, &actions, _1, _2)],
178 "\"\"\""
179 )
180 ;
181
182 // Note: Unlike escaped_comment and ignore, this doesn't
183 // swallow preceeding whitespace.
184 pass_thru_comment
185 = "#=" >> (cl::eps_p - '=')
186 >> ( *(cl::anychar_p - cl::eol_p)
187 >> (cl::eol_p | cl::end_p)
188 ) [boost::bind(&actions_type::mark, &actions, _1, _2)]
189 | cl::confix_p(
190 "\"\"\"=" >> (cl::eps_p - '='),
191 (*cl::anychar_p) [boost::bind(&actions_type::mark, &actions, _1, _2)],
192 "\"\"\""
193 )
194 ;
195 }
196
197 cl::rule<Scanner>
198 start_, identifier, code_elements, start_snippet, end_snippet,
199 escaped_comment, pass_thru_comment, ignore;
200
201 cl::rule<Scanner> const&
202 start() const { return start_; }
203 };
204
205 actions_type& actions;
206 };
207
208 struct cpp_code_snippet_grammar
209 : cl::grammar<cpp_code_snippet_grammar>
210 {
211 typedef code_snippet_actions actions_type;
212
213 cpp_code_snippet_grammar(actions_type & actions)
214 : actions(actions)
215 {}
216
217 template <typename Scanner>
218 struct definition
219 {
220 definition(cpp_code_snippet_grammar const& self)
221 {
222 actions_type& actions = self.actions;
223
224 start_ = (*code_elements) [boost::bind(&actions_type::end_file, &actions, _1, _2)]
225 ;
226
227 identifier =
228 (cl::alpha_p | '_') >> *(cl::alnum_p | '_')
229 ;
230
231 code_elements =
232 start_snippet [boost::bind(&actions_type::start_snippet, &actions, _1, _2)]
233 | end_snippet [boost::bind(&actions_type::end_snippet, &actions, _1, _2)]
234 | escaped_comment [boost::bind(&actions_type::escaped_comment, &actions, _1, _2)]
235 | ignore [boost::bind(&actions_type::append_code, &actions, _1, _2)]
236 | pass_thru_comment [boost::bind(&actions_type::pass_thru, &actions, _1, _2)]
237 | cl::anychar_p
238 ;
239
240 start_snippet =
241 *cl::blank_p
242 >> !(cl::eol_p >> *cl::blank_p)
243 >> "//["
244 >> *cl::blank_p
245 >> identifier [boost::bind(&actions_type::mark, &actions, _1, _2)]
246 >> *(cl::anychar_p - cl::eol_p)
247 |
248 *cl::blank_p
249 >> cl::eol_p
250 >> *cl::blank_p
251 >> "/*["
252 >> *cl::space_p
253 >> identifier [boost::bind(&actions_type::mark, &actions, _1, _2)]
254 >> *cl::space_p
255 >> "*/"
256 >> *cl::blank_p
257 >> cl::eps_p(cl::eol_p)
258 |
259 "/*["
260 >> *cl::space_p
261 >> identifier [boost::bind(&actions_type::mark, &actions, _1, _2)]
262 >> *cl::space_p
263 >> "*/"
264 ;
265
266 end_snippet =
267 *cl::blank_p
268 >> !(cl::eol_p >> *cl::blank_p)
269 >> "//]"
270 >> *(cl::anychar_p - cl::eol_p)
271 |
272 *cl::blank_p
273 >> cl::eol_p
274 >> *cl::blank_p
275 >> "/*]*/"
276 >> *cl::blank_p
277 >> cl::eps_p(cl::eol_p)
278 |
279 "/*[*/"
280 ;
281
282 ignore
283 = cl::confix_p(
284 *cl::blank_p >> "//<-",
285 *cl::anychar_p,
286 "//->"
287 )
288 >> *cl::blank_p
289 >> cl::eol_p
290 | cl::confix_p(
291 "/*<-*/",
292 *cl::anychar_p,
293 "/*->*/"
294 )
295 | cl::confix_p(
296 "/*<-",
297 *cl::anychar_p,
298 "->*/"
299 )
300 ;
301
302 escaped_comment
303 = cl::confix_p(
304 *cl::space_p >> "//`",
305 (*cl::anychar_p) [boost::bind(&actions_type::mark, &actions, _1, _2)],
306 (cl::eol_p | cl::end_p)
307 )
308 | cl::confix_p(
309 *cl::space_p >> "/*`",
310 (*cl::anychar_p) [boost::bind(&actions_type::mark, &actions, _1, _2)],
311 "*/"
312 )
313 ;
314
315 // Note: Unlike escaped_comment and ignore, this doesn't
316 // swallow preceeding whitespace.
317 pass_thru_comment
318 = "//=" >> (cl::eps_p - '=')
319 >> ( *(cl::anychar_p - cl::eol_p)
320 >> (cl::eol_p | cl::end_p)
321 ) [boost::bind(&actions_type::mark, &actions, _1, _2)]
322 | cl::confix_p(
323 "/*=" >> (cl::eps_p - '='),
324 (*cl::anychar_p) [boost::bind(&actions_type::mark, &actions, _1, _2)],
325 "*/"
326 )
327 ;
328 }
329
330 cl::rule<Scanner>
331 start_, identifier, code_elements, start_snippet, end_snippet,
332 escaped_comment, pass_thru_comment, ignore;
333
334 cl::rule<Scanner> const&
335 start() const { return start_; }
336 };
337
338 actions_type& actions;
339 };
340
341 int load_snippets(
342 fs::path const& filename
343 , std::vector<template_symbol>& storage // snippets are stored in a
344 // vector of template_symbols
345 , std::string const& extension
346 , value::tag_type load_type)
347 {
348 assert(load_type == block_tags::include ||
349 load_type == block_tags::import);
350
351 bool is_python = extension == ".py";
352 code_snippet_actions a(storage, load(filename, qbk_version_n), is_python ? "[python]" : "[c++]");
353
354 string_iterator first(a.source_file->source().begin());
355 string_iterator last(a.source_file->source().end());
356
357 cl::parse_info<string_iterator> info;
358
359 if(is_python) {
360 info = boost::spirit::classic::parse(first, last, python_code_snippet_grammar(a));
361 }
362 else {
363 info = boost::spirit::classic::parse(first, last, cpp_code_snippet_grammar(a));
364 }
365
366 assert(info.full);
367 return a.error_count;
368 }
369
370 void code_snippet_actions::append_code(string_iterator first, string_iterator last)
371 {
372 assert(last_code_pos <= first);
373
374 if(snippet_stack) {
375 if (last_code_pos != first) {
376 if (!in_code)
377 {
378 content.add_at_pos("\n\n", last_code_pos);
379 content.add_at_pos(source_type, last_code_pos);
380 content.add_at_pos("```\n", last_code_pos);
381
382 in_code = true;
383 }
384
385 content.add(boost::string_ref(last_code_pos, first - last_code_pos));
386 }
387 }
388
389 last_code_pos = last;
390 }
391
392 void code_snippet_actions::close_code()
393 {
394 if (!snippet_stack) return;
395
396 if (in_code)
397 {
398 content.add_at_pos("\n```\n\n", last_code_pos);
399 in_code = false;
400 }
401 }
402
403 void code_snippet_actions::mark(string_iterator first, string_iterator last)
404 {
405 mark_begin = first;
406 mark_end = last;
407 }
408
409 void code_snippet_actions::pass_thru(string_iterator first, string_iterator last)
410 {
411 if(!snippet_stack) return;
412 append_code(first, last);
413
414 if (!in_code)
415 {
416 content.add_at_pos("\n\n", first);
417 content.add_at_pos(source_type, first);
418 content.add_at_pos("```\n", first);
419 in_code = true;
420 }
421
422 content.add(boost::string_ref(mark_begin, mark_end - mark_begin));
423 }
424
425 void code_snippet_actions::escaped_comment(string_iterator first, string_iterator last)
426 {
427 append_code(first, last);
428 close_code();
429
430 if (mark_begin != mark_end)
431 {
432 if (!snippet_stack)
433 {
434 start_snippet_impl("!", first);
435 }
436
437 snippet_data& snippet = *snippet_stack;
438
439 content.add_at_pos("\n", mark_begin);
440 content.unindent_and_add(boost::string_ref(mark_begin, mark_end - mark_begin));
441
442 if (snippet.id == "!")
443 {
444 end_snippet_impl(last);
445 }
446 }
447 }
448
449 void code_snippet_actions::start_snippet(string_iterator first, string_iterator last)
450 {
451 append_code(first, last);
452 start_snippet_impl(std::string(mark_begin, mark_end), first);
453 }
454
455 void code_snippet_actions::end_snippet(string_iterator first, string_iterator last)
456 {
457 append_code(first, last);
458
459 if(!snippet_stack) {
460 if (qbk_version_n >= 106u) {
461 detail::outerr(source_file, first)
462 << "Mismatched end snippet."
463 << std::endl;
464 ++error_count;
465 }
466 else {
467 detail::outwarn(source_file, first)
468 << "Mismatched end snippet."
469 << std::endl;
470 }
471 return;
472 }
473
474 end_snippet_impl(first);
475 }
476
477 void code_snippet_actions::end_file(string_iterator, string_iterator pos)
478 {
479 append_code(pos, pos);
480 close_code();
481
482 while (snippet_stack) {
483 if (qbk_version_n >= 106u) {
484 detail::outerr(source_file->path)
485 << "Unclosed snippet '"
486 << snippet_stack->id
487 << "'"
488 << std::endl;
489 ++error_count;
490 }
491 else {
492 detail::outwarn(source_file->path)
493 << "Unclosed snippet '"
494 << snippet_stack->id
495 << "'"
496 << std::endl;
497 }
498
499 end_snippet_impl(pos);
500 }
501 }
502
503 void code_snippet_actions::start_snippet_impl(std::string const& id,
504 string_iterator position)
505 {
506 push_snippet_data(id, position);
507 }
508
509 void code_snippet_actions::end_snippet_impl(string_iterator position)
510 {
511 assert(snippet_stack);
512
513 boost::shared_ptr<snippet_data> snippet = pop_snippet_data();
514
515 mapped_file_builder f;
516 f.start(source_file);
517 if (snippet->start_code) {
518 f.add_at_pos("\n\n", snippet->source_pos);
519 f.add_at_pos(source_type, snippet->source_pos);
520 f.add_at_pos("```\n", snippet->source_pos);
521 }
522 f.add(content, snippet->start_pos, content.get_pos());
523 if (in_code) {
524 f.add_at_pos("\n```\n\n", position);
525 }
526
527 std::vector<std::string> params;
528
529 file_ptr body = f.release();
530
531 storage.push_back(template_symbol(snippet->id, params,
532 qbk_value(body, body->source().begin(), body->source().end(),
533 template_tags::snippet)));
534 }
535}