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/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"
21 #include "native_text.hpp"
25 namespace cl
= boost::spirit::classic
;
27 struct code_snippet_actions
29 code_snippet_actions(std::vector
<template_symbol
>& storage
,
31 char const* source_type
)
32 : last_code_pos(source_file
->source().begin())
36 , source_file(source_file
)
37 , source_type(source_type
)
40 source_file
->is_code_snippets
= true;
41 content
.start(source_file
);
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
);
53 void append_code(string_iterator first
, string_iterator last
);
58 snippet_data(std::string
const& id
)
65 string_iterator source_pos
;
66 mapped_file_builder::pos start_pos
;
67 boost::shared_ptr
<snippet_data
> next
;
70 void push_snippet_data(std::string
const& id
,
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();
81 boost::shared_ptr
<snippet_data
> pop_snippet_data()
83 boost::shared_ptr
<snippet_data
> snippet(snippet_stack
);
84 snippet_stack
= snippet
->next
;
85 snippet
->next
.reset();
89 mapped_file_builder content
;
90 boost::string_ref::const_iterator mark_begin
, mark_end
;
91 boost::string_ref::const_iterator last_code_pos
;
93 boost::shared_ptr
<snippet_data
> snippet_stack
;
94 std::vector
<template_symbol
>& storage
;
96 char const* const source_type
;
100 struct python_code_snippet_grammar
101 : cl::grammar
<python_code_snippet_grammar
>
103 typedef code_snippet_actions actions_type
;
105 python_code_snippet_grammar(actions_type
& actions
)
109 template <typename Scanner
>
112 typedef code_snippet_actions actions_type
;
114 definition(python_code_snippet_grammar
const& self
)
117 actions_type
& actions
= self
.actions
;
119 start_
= (*code_elements
) [boost::bind(&actions_type::end_file
, &actions
, _1
, _2
)]
123 (cl::alpha_p
| '_') >> *(cl::alnum_p
| '_')
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
)]
137 >> !(cl::eol_p
>> *cl::blank_p
)
140 >> identifier
[boost::bind(&actions_type::mark
, &actions
, _1
, _2
)]
141 >> *(cl::anychar_p
- cl::eol_p
)
146 >> !(cl::eol_p
>> *cl::blank_p
)
148 >> *(cl::anychar_p
- cl::eol_p
)
153 *cl::blank_p
>> "#<-",
155 "#->" >> *cl::blank_p
>> (cl::eol_p
| cl::end_p
)
171 *cl::space_p
>> "#`",
172 (*cl::anychar_p
) [boost::bind(&actions_type::mark
, &actions
, _1
, _2
)],
173 (cl::eol_p
| cl::end_p
)
176 *cl::space_p
>> "\"\"\"`",
177 (*cl::anychar_p
) [boost::bind(&actions_type::mark
, &actions
, _1
, _2
)],
182 // Note: Unlike escaped_comment and ignore, this doesn't
183 // swallow preceeding whitespace.
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
)]
190 "\"\"\"=" >> (cl::eps_p
- '='),
191 (*cl::anychar_p
) [boost::bind(&actions_type::mark
, &actions
, _1
, _2
)],
198 start_
, identifier
, code_elements
, start_snippet
, end_snippet
,
199 escaped_comment
, pass_thru_comment
, ignore
;
201 cl::rule
<Scanner
> const&
202 start() const { return start_
; }
205 actions_type
& actions
;
208 struct cpp_code_snippet_grammar
209 : cl::grammar
<cpp_code_snippet_grammar
>
211 typedef code_snippet_actions actions_type
;
213 cpp_code_snippet_grammar(actions_type
& actions
)
217 template <typename Scanner
>
220 definition(cpp_code_snippet_grammar
const& self
)
222 actions_type
& actions
= self
.actions
;
224 start_
= (*code_elements
) [boost::bind(&actions_type::end_file
, &actions
, _1
, _2
)]
228 (cl::alpha_p
| '_') >> *(cl::alnum_p
| '_')
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
)]
242 >> !(cl::eol_p
>> *cl::blank_p
)
245 >> identifier
[boost::bind(&actions_type::mark
, &actions
, _1
, _2
)]
246 >> *(cl::anychar_p
- cl::eol_p
)
253 >> identifier
[boost::bind(&actions_type::mark
, &actions
, _1
, _2
)]
257 >> cl::eps_p(cl::eol_p
)
261 >> identifier
[boost::bind(&actions_type::mark
, &actions
, _1
, _2
)]
268 >> !(cl::eol_p
>> *cl::blank_p
)
270 >> *(cl::anychar_p
- cl::eol_p
)
277 >> cl::eps_p(cl::eol_p
)
284 *cl::blank_p
>> "//<-",
304 *cl::space_p
>> "//`",
305 (*cl::anychar_p
) [boost::bind(&actions_type::mark
, &actions
, _1
, _2
)],
306 (cl::eol_p
| cl::end_p
)
309 *cl::space_p
>> "/*`",
310 (*cl::anychar_p
) [boost::bind(&actions_type::mark
, &actions
, _1
, _2
)],
315 // Note: Unlike escaped_comment and ignore, this doesn't
316 // swallow preceeding whitespace.
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
)]
323 "/*=" >> (cl::eps_p
- '='),
324 (*cl::anychar_p
) [boost::bind(&actions_type::mark
, &actions
, _1
, _2
)],
331 start_
, identifier
, code_elements
, start_snippet
, end_snippet
,
332 escaped_comment
, pass_thru_comment
, ignore
;
334 cl::rule
<Scanner
> const&
335 start() const { return start_
; }
338 actions_type
& actions
;
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
)
348 assert(load_type
== block_tags::include
||
349 load_type
== block_tags::import
);
351 bool is_python
= extension
== ".py";
352 code_snippet_actions
a(storage
, load(filename
, qbk_version_n
), is_python
? "[python]" : "[c++]");
354 string_iterator
first(a
.source_file
->source().begin());
355 string_iterator
last(a
.source_file
->source().end());
357 cl::parse_info
<string_iterator
> info
;
360 info
= boost::spirit::classic::parse(first
, last
, python_code_snippet_grammar(a
));
363 info
= boost::spirit::classic::parse(first
, last
, cpp_code_snippet_grammar(a
));
367 return a
.error_count
;
370 void code_snippet_actions::append_code(string_iterator first
, string_iterator last
)
372 assert(last_code_pos
<= first
);
375 if (last_code_pos
!= first
) {
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
);
385 content
.add(boost::string_ref(last_code_pos
, first
- last_code_pos
));
389 last_code_pos
= last
;
392 void code_snippet_actions::close_code()
394 if (!snippet_stack
) return;
398 content
.add_at_pos("\n```\n\n", last_code_pos
);
403 void code_snippet_actions::mark(string_iterator first
, string_iterator last
)
409 void code_snippet_actions::pass_thru(string_iterator first
, string_iterator last
)
411 if(!snippet_stack
) return;
412 append_code(first
, last
);
416 content
.add_at_pos("\n\n", first
);
417 content
.add_at_pos(source_type
, first
);
418 content
.add_at_pos("```\n", first
);
422 content
.add(boost::string_ref(mark_begin
, mark_end
- mark_begin
));
425 void code_snippet_actions::escaped_comment(string_iterator first
, string_iterator last
)
427 append_code(first
, last
);
430 if (mark_begin
!= mark_end
)
434 start_snippet_impl("!", first
);
437 snippet_data
& snippet
= *snippet_stack
;
439 content
.add_at_pos("\n", mark_begin
);
440 content
.unindent_and_add(boost::string_ref(mark_begin
, mark_end
- mark_begin
));
442 if (snippet
.id
== "!")
444 end_snippet_impl(last
);
449 void code_snippet_actions::start_snippet(string_iterator first
, string_iterator last
)
451 append_code(first
, last
);
452 start_snippet_impl(std::string(mark_begin
, mark_end
), first
);
455 void code_snippet_actions::end_snippet(string_iterator first
, string_iterator last
)
457 append_code(first
, last
);
460 if (qbk_version_n
>= 106u) {
461 detail::outerr(source_file
, first
)
462 << "Mismatched end snippet."
467 detail::outwarn(source_file
, first
)
468 << "Mismatched end snippet."
474 end_snippet_impl(first
);
477 void code_snippet_actions::end_file(string_iterator
, string_iterator pos
)
479 append_code(pos
, pos
);
482 while (snippet_stack
) {
483 if (qbk_version_n
>= 106u) {
484 detail::outerr(source_file
->path
)
485 << "Unclosed snippet '"
492 detail::outwarn(source_file
->path
)
493 << "Unclosed snippet '"
499 end_snippet_impl(pos
);
503 void code_snippet_actions::start_snippet_impl(std::string
const& id
,
504 string_iterator position
)
506 push_snippet_data(id
, position
);
509 void code_snippet_actions::end_snippet_impl(string_iterator position
)
511 assert(snippet_stack
);
513 boost::shared_ptr
<snippet_data
> snippet
= pop_snippet_data();
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
);
522 f
.add(content
, snippet
->start_pos
, content
.get_pos());
524 f
.add_at_pos("\n```\n\n", position
);
527 std::vector
<std::string
> params
;
529 file_ptr body
= f
.release();
531 storage
.push_back(template_symbol(snippet
->id
, params
,
532 qbk_value(body
, body
->source().begin(), body
->source().end(),
533 template_tags::snippet
)));