]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/tools/quickbook/src/code_snippet.cpp
import new upstream nautilus stable release 14.2.8
[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
11fdf7f2
TL
10#include <boost/bind.hpp>
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
FG
22
23namespace quickbook
24{
25 namespace cl = boost::spirit::classic;
26
27 struct code_snippet_actions
28 {
11fdf7f2
TL
29 code_snippet_actions(
30 std::vector<template_symbol>& storage_,
31 file_ptr source_file_,
32 char const* source_type_)
b32b8144 33 : last_code_pos(source_file_->source().begin())
7c673cae
FG
34 , in_code(false)
35 , snippet_stack()
b32b8144
FG
36 , storage(storage_)
37 , source_file(source_file_)
38 , source_type(source_type_)
7c673cae
FG
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);
11fdf7f2 53
7c673cae
FG
54 void append_code(string_iterator first, string_iterator last);
55 void close_code();
56
57 struct snippet_data
58 {
11fdf7f2
TL
59 snippet_data(std::string const& id_) : id(id_), start_code(false) {}
60
7c673cae
FG
61 std::string id;
62 bool start_code;
63 string_iterator source_pos;
b32b8144 64 mapped_file_builder::pos_type start_pos;
7c673cae
FG
65 boost::shared_ptr<snippet_data> next;
66 };
11fdf7f2
TL
67
68 void push_snippet_data(std::string const& id, string_iterator pos)
7c673cae
FG
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;
b32b8144
FG
87 string_iterator mark_begin, mark_end;
88 string_iterator last_code_pos;
7c673cae
FG
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;
7c673cae 101
11fdf7f2
TL
102 python_code_snippet_grammar(actions_type& actions_) : actions(actions_)
103 {
104 }
105
106 template <typename Scanner> struct definition
7c673cae
FG
107 {
108 typedef code_snippet_actions actions_type;
11fdf7f2 109
7c673cae
FG
110 definition(python_code_snippet_grammar const& self)
111 {
11fdf7f2 112 // clang-format off
7c673cae 113
b32b8144 114 start_ = (*code_elements) [boost::bind(&actions_type::end_file, &self.actions, _1, _2)]
7c673cae
FG
115 ;
116
117 identifier =
118 (cl::alpha_p | '_') >> *(cl::alnum_p | '_')
119 ;
120
121 code_elements =
b32b8144
FG
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)]
7c673cae
FG
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
b32b8144 135 >> identifier [boost::bind(&actions_type::mark, &self.actions, _1, _2)]
7c673cae
FG
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 >> "#`",
b32b8144 167 (*cl::anychar_p) [boost::bind(&actions_type::mark, &self.actions, _1, _2)],
7c673cae
FG
168 (cl::eol_p | cl::end_p)
169 )
170 | cl::confix_p(
171 *cl::space_p >> "\"\"\"`",
b32b8144 172 (*cl::anychar_p) [boost::bind(&actions_type::mark, &self.actions, _1, _2)],
7c673cae
FG
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)
b32b8144 183 ) [boost::bind(&actions_type::mark, &self.actions, _1, _2)]
7c673cae
FG
184 | cl::confix_p(
185 "\"\"\"=" >> (cl::eps_p - '='),
b32b8144 186 (*cl::anychar_p) [boost::bind(&actions_type::mark, &self.actions, _1, _2)],
7c673cae
FG
187 "\"\"\""
188 )
189 ;
11fdf7f2
TL
190
191 // clang-format on
7c673cae
FG
192 }
193
11fdf7f2
TL
194 cl::rule<Scanner> start_, identifier, code_elements, start_snippet,
195 end_snippet, escaped_comment, pass_thru_comment, ignore;
7c673cae 196
11fdf7f2 197 cl::rule<Scanner> const& start() const { return start_; }
7c673cae
FG
198 };
199
200 actions_type& actions;
11fdf7f2 201 };
7c673cae 202
11fdf7f2 203 struct cpp_code_snippet_grammar : cl::grammar<cpp_code_snippet_grammar>
7c673cae
FG
204 {
205 typedef code_snippet_actions actions_type;
7c673cae 206
11fdf7f2
TL
207 cpp_code_snippet_grammar(actions_type& actions_) : actions(actions_) {}
208
209 template <typename Scanner> struct definition
7c673cae
FG
210 {
211 definition(cpp_code_snippet_grammar const& self)
212 {
11fdf7f2
TL
213 // clang-format off
214
b32b8144 215 start_ = (*code_elements) [boost::bind(&actions_type::end_file, &self.actions, _1, _2)]
7c673cae
FG
216 ;
217
218 identifier =
219 (cl::alpha_p | '_') >> *(cl::alnum_p | '_')
220 ;
221
222 code_elements =
b32b8144
FG
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)]
7c673cae
FG
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
b32b8144 236 >> identifier [boost::bind(&actions_type::mark, &self.actions, _1, _2)]
7c673cae
FG
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
b32b8144 244 >> identifier [boost::bind(&actions_type::mark, &self.actions, _1, _2)]
7c673cae
FG
245 >> *cl::space_p
246 >> "*/"
247 >> *cl::blank_p
248 >> cl::eps_p(cl::eol_p)
249 |
250 "/*["
251 >> *cl::space_p
b32b8144 252 >> identifier [boost::bind(&actions_type::mark, &self.actions, _1, _2)]
7c673cae
FG
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 >> "//`",
b32b8144 296 (*cl::anychar_p) [boost::bind(&actions_type::mark, &self.actions, _1, _2)],
7c673cae
FG
297 (cl::eol_p | cl::end_p)
298 )
299 | cl::confix_p(
300 *cl::space_p >> "/*`",
b32b8144 301 (*cl::anychar_p) [boost::bind(&actions_type::mark, &self.actions, _1, _2)],
7c673cae
FG
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)
b32b8144 312 ) [boost::bind(&actions_type::mark, &self.actions, _1, _2)]
7c673cae
FG
313 | cl::confix_p(
314 "/*=" >> (cl::eps_p - '='),
b32b8144 315 (*cl::anychar_p) [boost::bind(&actions_type::mark, &self.actions, _1, _2)],
7c673cae
FG
316 "*/"
317 )
318 ;
11fdf7f2
TL
319
320 // clang-format on
7c673cae
FG
321 }
322
11fdf7f2
TL
323 cl::rule<Scanner> start_, identifier, code_elements, start_snippet,
324 end_snippet, escaped_comment, pass_thru_comment, ignore;
7c673cae 325
11fdf7f2 326 cl::rule<Scanner> const& start() const { return start_; }
7c673cae
FG
327 };
328
329 actions_type& actions;
330 };
331
332 int load_snippets(
11fdf7f2
TL
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)
7c673cae 339 {
92f5a8d4 340 ignore_variable(&load_type); // Avoid unreferenced parameter warning.
11fdf7f2
TL
341 assert(
342 load_type == block_tags::include ||
7c673cae
FG
343 load_type == block_tags::import);
344
b32b8144 345 bool is_python = extension == ".py" || extension == ".jam";
11fdf7f2
TL
346 code_snippet_actions a(
347 storage, load(filename, qbk_version_n),
348 is_python ? "[python]" : "[c++]");
7c673cae
FG
349
350 string_iterator first(a.source_file->source().begin());
351 string_iterator last(a.source_file->source().end());
352
353 cl::parse_info<string_iterator> info;
354
11fdf7f2
TL
355 if (is_python) {
356 info = boost::spirit::classic::parse(
357 first, last, python_code_snippet_grammar(a));
7c673cae
FG
358 }
359 else {
11fdf7f2
TL
360 info = boost::spirit::classic::parse(
361 first, last, cpp_code_snippet_grammar(a));
7c673cae
FG
362 }
363
364 assert(info.full);
365 return a.error_count;
366 }
367
11fdf7f2
TL
368 void code_snippet_actions::append_code(
369 string_iterator first, string_iterator last)
7c673cae
FG
370 {
371 assert(last_code_pos <= first);
372
11fdf7f2 373 if (snippet_stack) {
7c673cae 374 if (last_code_pos != first) {
11fdf7f2 375 if (!in_code) {
7c673cae
FG
376 content.add_at_pos("\n\n", last_code_pos);
377 content.add_at_pos(source_type, last_code_pos);
378 content.add_at_pos("```\n", last_code_pos);
379
380 in_code = true;
381 }
382
11fdf7f2
TL
383 content.add(quickbook::string_view(
384 last_code_pos, first - last_code_pos));
7c673cae
FG
385 }
386 }
11fdf7f2 387
7c673cae
FG
388 last_code_pos = last;
389 }
11fdf7f2 390
7c673cae
FG
391 void code_snippet_actions::close_code()
392 {
393 if (!snippet_stack) return;
11fdf7f2
TL
394
395 if (in_code) {
7c673cae
FG
396 content.add_at_pos("\n```\n\n", last_code_pos);
397 in_code = false;
398 }
399 }
400
401 void code_snippet_actions::mark(string_iterator first, string_iterator last)
402 {
403 mark_begin = first;
404 mark_end = last;
405 }
406
11fdf7f2
TL
407 void code_snippet_actions::pass_thru(
408 string_iterator first, string_iterator last)
7c673cae 409 {
11fdf7f2 410 if (!snippet_stack) return;
7c673cae
FG
411 append_code(first, last);
412
11fdf7f2 413 if (!in_code) {
7c673cae
FG
414 content.add_at_pos("\n\n", first);
415 content.add_at_pos(source_type, first);
416 content.add_at_pos("```\n", first);
417 in_code = true;
418 }
419
b32b8144 420 content.add(quickbook::string_view(mark_begin, mark_end - mark_begin));
7c673cae
FG
421 }
422
11fdf7f2
TL
423 void code_snippet_actions::escaped_comment(
424 string_iterator first, string_iterator last)
7c673cae
FG
425 {
426 append_code(first, last);
427 close_code();
428
11fdf7f2
TL
429 if (mark_begin != mark_end) {
430 if (!snippet_stack) {
7c673cae
FG
431 start_snippet_impl("!", first);
432 }
11fdf7f2 433
7c673cae
FG
434 snippet_data& snippet = *snippet_stack;
435
436 content.add_at_pos("\n", mark_begin);
11fdf7f2
TL
437 content.unindent_and_add(
438 quickbook::string_view(mark_begin, mark_end - mark_begin));
7c673cae 439
11fdf7f2 440 if (snippet.id == "!") {
7c673cae
FG
441 end_snippet_impl(last);
442 }
443 }
444 }
445
11fdf7f2
TL
446 void code_snippet_actions::start_snippet(
447 string_iterator first, string_iterator last)
7c673cae
FG
448 {
449 append_code(first, last);
450 start_snippet_impl(std::string(mark_begin, mark_end), first);
451 }
452
11fdf7f2
TL
453 void code_snippet_actions::end_snippet(
454 string_iterator first, string_iterator last)
7c673cae
FG
455 {
456 append_code(first, last);
457
11fdf7f2 458 if (!snippet_stack) {
7c673cae
FG
459 if (qbk_version_n >= 106u) {
460 detail::outerr(source_file, first)
11fdf7f2 461 << "Mismatched end snippet." << std::endl;
7c673cae
FG
462 ++error_count;
463 }
464 else {
465 detail::outwarn(source_file, first)
11fdf7f2 466 << "Mismatched end snippet." << std::endl;
7c673cae
FG
467 }
468 return;
469 }
470
471 end_snippet_impl(first);
472 }
11fdf7f2 473
7c673cae
FG
474 void code_snippet_actions::end_file(string_iterator, string_iterator pos)
475 {
476 append_code(pos, pos);
477 close_code();
478
479 while (snippet_stack) {
480 if (qbk_version_n >= 106u) {
481 detail::outerr(source_file->path)
11fdf7f2 482 << "Unclosed snippet '" << snippet_stack->id << "'"
7c673cae
FG
483 << std::endl;
484 ++error_count;
485 }
486 else {
487 detail::outwarn(source_file->path)
11fdf7f2 488 << "Unclosed snippet '" << snippet_stack->id << "'"
7c673cae
FG
489 << std::endl;
490 }
11fdf7f2 491
7c673cae
FG
492 end_snippet_impl(pos);
493 }
494 }
495
11fdf7f2
TL
496 void code_snippet_actions::start_snippet_impl(
497 std::string const& id, string_iterator position)
7c673cae
FG
498 {
499 push_snippet_data(id, position);
500 }
501
502 void code_snippet_actions::end_snippet_impl(string_iterator position)
503 {
504 assert(snippet_stack);
505
506 boost::shared_ptr<snippet_data> snippet = pop_snippet_data();
507
508 mapped_file_builder f;
509 f.start(source_file);
510 if (snippet->start_code) {
511 f.add_at_pos("\n\n", snippet->source_pos);
512 f.add_at_pos(source_type, snippet->source_pos);
513 f.add_at_pos("```\n", snippet->source_pos);
514 }
515 f.add(content, snippet->start_pos, content.get_pos());
516 if (in_code) {
517 f.add_at_pos("\n```\n\n", position);
518 }
519
520 std::vector<std::string> params;
521
522 file_ptr body = f.release();
523
11fdf7f2
TL
524 storage.push_back(template_symbol(
525 snippet->id, params,
526 qbk_value(
527 body, body->source().begin(), body->source().end(),
7c673cae
FG
528 template_tags::snippet)));
529 }
530}