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