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