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