1 /*=============================================================================
2 Copyright (c) 2006 Joel de Guzman
3 http://spirit.sourceforge.net/
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 =============================================================================*/
10 #include <boost/bind.hpp>
11 #include <boost/shared_ptr.hpp>
12 #include <boost/spirit/include/classic_actor.hpp>
13 #include <boost/spirit/include/classic_confix.hpp>
14 #include <boost/spirit/include/classic_core.hpp>
15 #include "actions.hpp"
16 #include "block_tags.hpp"
20 #include "template_stack.hpp"
25 namespace cl
= boost::spirit::classic
;
27 struct code_snippet_actions
30 std::vector
<template_symbol
>& storage_
,
31 file_ptr source_file_
,
32 char const* source_type_
)
33 : last_code_pos(source_file_
->source().begin())
37 , source_file(source_file_
)
38 , source_type(source_type_
)
41 source_file
->is_code_snippets
= true;
42 content
.start(source_file
);
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
);
54 void append_code(string_iterator first
, string_iterator last
);
59 snippet_data(std::string
const& id_
) : id(id_
), start_code(false) {}
63 string_iterator source_pos
;
64 mapped_file_builder::pos_type start_pos
;
65 boost::shared_ptr
<snippet_data
> next
;
68 void push_snippet_data(std::string
const& id
, string_iterator pos
)
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();
78 boost::shared_ptr
<snippet_data
> pop_snippet_data()
80 boost::shared_ptr
<snippet_data
> snippet(snippet_stack
);
81 snippet_stack
= snippet
->next
;
82 snippet
->next
.reset();
86 mapped_file_builder content
;
87 string_iterator mark_begin
, mark_end
;
88 string_iterator last_code_pos
;
90 boost::shared_ptr
<snippet_data
> snippet_stack
;
91 std::vector
<template_symbol
>& storage
;
93 char const* const source_type
;
97 struct python_code_snippet_grammar
98 : cl::grammar
<python_code_snippet_grammar
>
100 typedef code_snippet_actions actions_type
;
102 python_code_snippet_grammar(actions_type
& actions_
) : actions(actions_
)
106 template <typename Scanner
> struct definition
108 typedef code_snippet_actions actions_type
;
110 definition(python_code_snippet_grammar
const& self
)
114 start_
= (*code_elements
) [boost::bind(&actions_type::end_file
, &self
.actions
, _1
, _2
)]
118 (cl::alpha_p
| '_') >> *(cl::alnum_p
| '_')
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
)]
132 >> !(cl::eol_p
>> *cl::blank_p
)
135 >> identifier
[boost::bind(&actions_type::mark
, &self
.actions
, _1
, _2
)]
136 >> *(cl::anychar_p
- cl::eol_p
)
141 >> !(cl::eol_p
>> *cl::blank_p
)
143 >> *(cl::anychar_p
- cl::eol_p
)
148 *cl::blank_p
>> "#<-",
150 "#->" >> *cl::blank_p
>> (cl::eol_p
| cl::end_p
)
166 *cl::space_p
>> "#`",
167 (*cl::anychar_p
) [boost::bind(&actions_type::mark
, &self
.actions
, _1
, _2
)],
168 (cl::eol_p
| cl::end_p
)
171 *cl::space_p
>> "\"\"\"`",
172 (*cl::anychar_p
) [boost::bind(&actions_type::mark
, &self
.actions
, _1
, _2
)],
177 // Note: Unlike escaped_comment and ignore, this doesn't
178 // swallow preceeding whitespace.
180 = "#=" >> (cl::eps_p
- '=')
181 >> ( *(cl::anychar_p
- cl::eol_p
)
182 >> (cl::eol_p
| cl::end_p
)
183 ) [boost::bind(&actions_type::mark
, &self
.actions
, _1
, _2
)]
185 "\"\"\"=" >> (cl::eps_p
- '='),
186 (*cl::anychar_p
) [boost::bind(&actions_type::mark
, &self
.actions
, _1
, _2
)],
194 cl::rule
<Scanner
> start_
, identifier
, code_elements
, start_snippet
,
195 end_snippet
, escaped_comment
, pass_thru_comment
, ignore
;
197 cl::rule
<Scanner
> const& start() const { return start_
; }
200 actions_type
& actions
;
203 struct cpp_code_snippet_grammar
: cl::grammar
<cpp_code_snippet_grammar
>
205 typedef code_snippet_actions actions_type
;
207 cpp_code_snippet_grammar(actions_type
& actions_
) : actions(actions_
) {}
209 template <typename Scanner
> struct definition
211 definition(cpp_code_snippet_grammar
const& self
)
215 start_
= (*code_elements
) [boost::bind(&actions_type::end_file
, &self
.actions
, _1
, _2
)]
219 (cl::alpha_p
| '_') >> *(cl::alnum_p
| '_')
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
)]
233 >> !(cl::eol_p
>> *cl::blank_p
)
236 >> identifier
[boost::bind(&actions_type::mark
, &self
.actions
, _1
, _2
)]
237 >> *(cl::anychar_p
- cl::eol_p
)
244 >> identifier
[boost::bind(&actions_type::mark
, &self
.actions
, _1
, _2
)]
248 >> cl::eps_p(cl::eol_p
)
252 >> identifier
[boost::bind(&actions_type::mark
, &self
.actions
, _1
, _2
)]
259 >> !(cl::eol_p
>> *cl::blank_p
)
261 >> *(cl::anychar_p
- cl::eol_p
)
268 >> cl::eps_p(cl::eol_p
)
275 *cl::blank_p
>> "//<-",
295 *cl::space_p
>> "//`",
296 (*cl::anychar_p
) [boost::bind(&actions_type::mark
, &self
.actions
, _1
, _2
)],
297 (cl::eol_p
| cl::end_p
)
300 *cl::space_p
>> "/*`",
301 (*cl::anychar_p
) [boost::bind(&actions_type::mark
, &self
.actions
, _1
, _2
)],
306 // Note: Unlike escaped_comment and ignore, this doesn't
307 // swallow preceeding whitespace.
309 = "//=" >> (cl::eps_p
- '=')
310 >> ( *(cl::anychar_p
- cl::eol_p
)
311 >> (cl::eol_p
| cl::end_p
)
312 ) [boost::bind(&actions_type::mark
, &self
.actions
, _1
, _2
)]
314 "/*=" >> (cl::eps_p
- '='),
315 (*cl::anychar_p
) [boost::bind(&actions_type::mark
, &self
.actions
, _1
, _2
)],
323 cl::rule
<Scanner
> start_
, identifier
, code_elements
, start_snippet
,
324 end_snippet
, escaped_comment
, pass_thru_comment
, ignore
;
326 cl::rule
<Scanner
> const& start() const { return start_
; }
329 actions_type
& actions
;
333 fs::path
const& filename
,
334 std::vector
<template_symbol
>& storage
// snippets are stored in a
335 // vector of template_symbols
337 std::string
const& extension
,
338 value::tag_type load_type
)
341 load_type
== block_tags::include
||
342 load_type
== block_tags::import
);
344 bool is_python
= extension
== ".py" || extension
== ".jam";
345 code_snippet_actions
a(
346 storage
, load(filename
, qbk_version_n
),
347 is_python
? "[python]" : "[c++]");
349 string_iterator
first(a
.source_file
->source().begin());
350 string_iterator
last(a
.source_file
->source().end());
352 cl::parse_info
<string_iterator
> info
;
355 info
= boost::spirit::classic::parse(
356 first
, last
, python_code_snippet_grammar(a
));
359 info
= boost::spirit::classic::parse(
360 first
, last
, cpp_code_snippet_grammar(a
));
364 return a
.error_count
;
367 void code_snippet_actions::append_code(
368 string_iterator first
, string_iterator last
)
370 assert(last_code_pos
<= first
);
373 if (last_code_pos
!= first
) {
375 content
.add_at_pos("\n\n", last_code_pos
);
376 content
.add_at_pos(source_type
, last_code_pos
);
377 content
.add_at_pos("```\n", last_code_pos
);
382 content
.add(quickbook::string_view(
383 last_code_pos
, first
- last_code_pos
));
387 last_code_pos
= last
;
390 void code_snippet_actions::close_code()
392 if (!snippet_stack
) return;
395 content
.add_at_pos("\n```\n\n", last_code_pos
);
400 void code_snippet_actions::mark(string_iterator first
, string_iterator last
)
406 void code_snippet_actions::pass_thru(
407 string_iterator first
, string_iterator last
)
409 if (!snippet_stack
) return;
410 append_code(first
, last
);
413 content
.add_at_pos("\n\n", first
);
414 content
.add_at_pos(source_type
, first
);
415 content
.add_at_pos("```\n", first
);
419 content
.add(quickbook::string_view(mark_begin
, mark_end
- mark_begin
));
422 void code_snippet_actions::escaped_comment(
423 string_iterator first
, string_iterator last
)
425 append_code(first
, last
);
428 if (mark_begin
!= mark_end
) {
429 if (!snippet_stack
) {
430 start_snippet_impl("!", first
);
433 snippet_data
& snippet
= *snippet_stack
;
435 content
.add_at_pos("\n", mark_begin
);
436 content
.unindent_and_add(
437 quickbook::string_view(mark_begin
, mark_end
- mark_begin
));
439 if (snippet
.id
== "!") {
440 end_snippet_impl(last
);
445 void code_snippet_actions::start_snippet(
446 string_iterator first
, string_iterator last
)
448 append_code(first
, last
);
449 start_snippet_impl(std::string(mark_begin
, mark_end
), first
);
452 void code_snippet_actions::end_snippet(
453 string_iterator first
, string_iterator last
)
455 append_code(first
, last
);
457 if (!snippet_stack
) {
458 if (qbk_version_n
>= 106u) {
459 detail::outerr(source_file
, first
)
460 << "Mismatched end snippet." << std::endl
;
464 detail::outwarn(source_file
, first
)
465 << "Mismatched end snippet." << std::endl
;
470 end_snippet_impl(first
);
473 void code_snippet_actions::end_file(string_iterator
, string_iterator pos
)
475 append_code(pos
, pos
);
478 while (snippet_stack
) {
479 if (qbk_version_n
>= 106u) {
480 detail::outerr(source_file
->path
)
481 << "Unclosed snippet '" << snippet_stack
->id
<< "'"
486 detail::outwarn(source_file
->path
)
487 << "Unclosed snippet '" << snippet_stack
->id
<< "'"
491 end_snippet_impl(pos
);
495 void code_snippet_actions::start_snippet_impl(
496 std::string
const& id
, string_iterator position
)
498 push_snippet_data(id
, position
);
501 void code_snippet_actions::end_snippet_impl(string_iterator position
)
503 assert(snippet_stack
);
505 boost::shared_ptr
<snippet_data
> snippet
= pop_snippet_data();
507 mapped_file_builder f
;
508 f
.start(source_file
);
509 if (snippet
->start_code
) {
510 f
.add_at_pos("\n\n", snippet
->source_pos
);
511 f
.add_at_pos(source_type
, snippet
->source_pos
);
512 f
.add_at_pos("```\n", snippet
->source_pos
);
514 f
.add(content
, snippet
->start_pos
, content
.get_pos());
516 f
.add_at_pos("\n```\n\n", position
);
519 std::vector
<std::string
> params
;
521 file_ptr body
= f
.release();
523 storage
.push_back(template_symbol(
526 body
, body
->source().begin(), body
->source().end(),
527 template_tags::snippet
)));