]>
Commit | Line | Data |
---|---|---|
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 |
23 | using namespace boost::placeholders; |
24 | ||
7c673cae FG |
25 | namespace 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 | } |