]>
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 | ||
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 | |
23 | namespace 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 | } |