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